aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2023-06-17 04:39:07 +0200
committerHugo Hörnquist <hugo@lysator.liu.se>2023-06-17 04:39:07 +0200
commitd475cd0b09057240821a4e00f5ec5dc1e5c4b7b1 (patch)
tree0a26a37183fc4848449089994d195c75564b4134
parentSimplify index generation. (diff)
downloadmuppet-strings-d475cd0b09057240821a4e00f5ec5dc1e5c4b7b1.tar.gz
muppet-strings-d475cd0b09057240821a4e00f5ec5dc1e5c4b7b1.tar.xz
Include inline documentation for ecah class/resource parameter.
-rw-r--r--muppet/data/__init__.py20
-rw-r--r--muppet/data/html.py26
-rw-r--r--muppet/format.py78
-rw-r--r--static/highlight.css4
-rw-r--r--static/style.css23
5 files changed, 133 insertions, 18 deletions
diff --git a/muppet/data/__init__.py b/muppet/data/__init__.py
index 9e685be..6666e5b 100644
--- a/muppet/data/__init__.py
+++ b/muppet/data/__init__.py
@@ -22,6 +22,7 @@ from typing import (
Markup: TypeAlias = Union[str,
'Tag',
+ 'Declaration',
'Link',
'ID',
'Documentation',
@@ -50,6 +51,20 @@ class Tag:
@dataclass
+class Declaration(Tag):
+ """
+ Superset of tag, containing declaration statements.
+
+ Mostly used for class and resource parameters.
+
+ :param variable:
+ Name of the variable being declared.
+ """
+
+ variable: str
+
+
+@dataclass
class Link:
"""An item which should link somewhere."""
@@ -103,6 +118,11 @@ def tag(item: Markup | Sequence[Markup], *tags: str) -> Tag:
return Tag(item, tags=tags)
+def declaration(item: Markup | Sequence[Markup], *tags: str, variable: str) -> Declaration:
+ """Mark name of variable in declaration."""
+ return Declaration(item, tags=tags, variable=variable)
+
+
def link(item: Markup, target: str) -> Link:
"""Create a new link element."""
return Link(item, target)
diff --git a/muppet/data/html.py b/muppet/data/html.py
index 67edef9..df4f0f2 100644
--- a/muppet/data/html.py
+++ b/muppet/data/html.py
@@ -2,6 +2,7 @@
from . import (
Tag,
+ Declaration,
Link,
ID,
Documentation,
@@ -11,10 +12,21 @@ from . import (
)
from collections.abc import Sequence
import html
+from dataclasses import dataclass, field
+@dataclass
class HTMLRenderer(Renderer):
- """Render the document into HTML."""
+ """
+ Render the document into HTML.
+
+ :param param_documentation:
+ A dictionary containing (rendered) documentation for each
+ parameter of the class or resource type currently being
+ rendered.
+ """
+
+ param_documentation: dict[str, str] = field(default_factory=dict)
def render_tag(self, tag: Tag) -> str:
"""Attaches all tags as classes in a span."""
@@ -26,8 +38,18 @@ class HTMLRenderer(Renderer):
else:
inner = render(self, tag.item)
+ out = ''
+ if isinstance(tag, Declaration):
+ if comment := self.param_documentation.get(tag.variable):
+ if isinstance(tag.item, list) \
+ and tag.item \
+ and isinstance(tag.item[0], Indentation):
+ out += render(self, tag.item[0])
+ out += f'<span class="comment">{comment.strip()}</span>\n'
+
tags = ' '.join(tag.tags)
- return f'<span class="{tags}">{inner}</span>'
+ out += f'<span class="{tags}">{inner}</span>'
+ return out
def render_link(self, link: Link) -> str:
"""Wrap the value in an anchor tag."""
diff --git a/muppet/format.py b/muppet/format.py
index c9bce7c..67aa555 100644
--- a/muppet/format.py
+++ b/muppet/format.py
@@ -10,6 +10,7 @@ from commonmark import commonmark
from subprocess import CalledProcessError
import html
import sys
+import re
from typing import (
Any,
Literal,
@@ -29,6 +30,7 @@ from .data import (
id,
link,
tag,
+ declaration,
render,
)
from .data.html import (
@@ -260,18 +262,20 @@ def parse(form: Any, indent: int, context: list[str]) -> Tag:
if 'params' in rest:
items += ['(', '\n']
for name, data in rest['params'].items():
- items += [ind(indent+1)]
+ decls: list[Markup] = []
+ decls += [ind(indent+1)]
if 'type' in data:
tt = parse(data['type'], indent+1, context)
- items += [tag(tt, 'type'),
+ decls += [tag(tt, 'type'),
' ']
- items += [tag(f'${name}', 'var')]
+ decls += [declare_var(name)]
if 'value' in data:
- items += [
+ decls += [
' ', '=', ' ',
# TODO this is a declaration
parse(data.get('value'), indent+1, context),
]
+ items += [declaration(decls, 'declaration', variable=name)]
items += [',', '\n']
items += [ind(indent), ')', ' ', '{', '\n']
else:
@@ -320,18 +324,20 @@ def parse(form: Any, indent: int, context: list[str]) -> Tag:
if params := rest.get('params'):
items += ['(', '\n']
for name, data in params.items():
- items += [ind(indent+1)]
+ decl: list[Markup] = []
+ decl += [ind(indent+1)]
if 'type' in data:
- items += [tag(parse(data['type'], indent, context),
- 'type'),
- ' ']
+ decl += [tag(parse(data['type'], indent, context),
+ 'type'),
+ ' ']
# print(f'<span class="var">${name}</span>', end='')
- items += [declare_var(name)]
+ decl += [declare_var(name)]
if 'value' in data:
- items += [
+ decl += [
' ', '=', ' ',
parse(data.get('value'), indent, context),
]
+ items += [declaration(decl, 'declaration', variable=name)]
items += [',', '\n']
items += [ind(indent), ')', ' ']
@@ -1050,8 +1056,20 @@ def print_docstring(name: str, docstring: dict[str, Any]) -> str:
for t in tags:
text = html.escape(t.get('text') or '')
if t['tag_name'] == 'example':
- out += f'<h3>{t["name"]}</h3>\n'
- out += f'<pre><code class="puppet">{text}</code></pre>\n'
+ if name := t.get('name'):
+ out += f'<h3>{name}</h3>\n'
+ out += f'<pre class="example"><code class="puppet">{text}</code></pre>\n'
+
+ # out += '<dl>'
+ # for t in tags:
+ # if t['tag_name'] == 'param':
+ # out += f"<dt>{t['name']}</dt>"
+ # if text := t.get('text'):
+ # text = re.sub(r'(NOTE|TODO)',
+ # r'<mark>\1</mark>',
+ # commonmark(text))
+ # out += f"<dd>{text}</dd>"
+ # out += '</dl>'
if 'text' in docstring:
out += '<div>'
@@ -1061,19 +1079,45 @@ def print_docstring(name: str, docstring: dict[str, Any]) -> str:
return out
-renderer = HTMLRenderer()
+def build_param_dict(docstring: dict[str, Any]) -> dict[str, str]:
+ """
+ Extract all parameter documentation from a docstring dict.
+
+ :param docstring:
+ The object present under 'docstring' in the information about
+ a single object (class, resource, ...) in the output of
+ `puppet strings`.
+
+ :returns:
+ A dictionary where the keys are the variables which have
+ documentation, and the value is the (formatted) documentation
+ for that key. Undocumented keys (even those with the tag, but
+ no text) are ommitted from the resulting dictionary.
+ """
+ if tags := docstring.get('tags'):
+ obj = {}
+ for t in tags:
+ if t['tag_name'] == 'param':
+ if text := t.get('text'):
+ obj[t['name']] = re.sub(r'(NOTE|TODO)',
+ r'<mark>\1</mark>',
+ commonmark(text))
+ return obj
+ else:
+ return {}
def format_class(d_type: dict[str, Any]) -> str:
"""Format Puppet class."""
+ t = parse_puppet(d_type['source'])
+ data = parse(t, 0, ['root'])
+ renderer = HTMLRenderer(build_param_dict(d_type['docstring']))
out = ''
name = d_type['name']
# print(name, file=sys.stderr)
- print_docstring(name, d_type['docstring'])
+ out += print_docstring(name, d_type['docstring'])
out += '<pre><code class="puppet">'
- t = parse_puppet(d_type['source'])
- data = parse(t, 0, ['root'])
out += render(renderer, data)
out += '</code></pre>'
return out
@@ -1086,6 +1130,7 @@ def format_type() -> str:
def format_type_alias(d_type: dict[str, Any]) -> str:
"""Format Puppet type alias."""
+ renderer = HTMLRenderer()
out = ''
name = d_type['name']
# print(name, file=sys.stderr)
@@ -1101,6 +1146,7 @@ def format_type_alias(d_type: dict[str, Any]) -> str:
def format_defined_type(d_type: dict[str, Any]) -> str:
"""Format Puppet defined type."""
+ renderer = HTMLRenderer(build_param_dict(d_type['docstring']))
out = ''
name = d_type['name']
# print(name, file=sys.stderr)
diff --git a/static/highlight.css b/static/highlight.css
index 4949f3b..b3aa5d0 100644
--- a/static/highlight.css
+++ b/static/highlight.css
@@ -43,3 +43,7 @@
.string {
color: olive;
}
+
+.comment {
+ color: grey;
+}
diff --git a/static/style.css b/static/style.css
index 43e4144..fb19678 100644
--- a/static/style.css
+++ b/static/style.css
@@ -57,3 +57,26 @@ code.json {
.overview-list p {
display: inline;
}
+
+.example {
+ background: lightgray;
+ padding: 1em;
+ border-radius: 1ex;
+}
+
+.comment {
+ border-left: 1ex;
+ border-left-style: dotted;
+ display: inline-block;
+ padding-left: 1em;
+ font-family: sans;
+ font-size: 80%;
+}
+
+.comment p:first-child {
+ margin-top: 0;
+}
+
+.comment p:last-child {
+ margin-bottom: 0;
+}