--- aar_doc/defaults.py +++ aar_doc/defaults.py @@ -15,7 +15,7 @@ from ruamel.yaml.scalarstring import LiteralScalarString, SingleQuotedScalarString yaml = YAML() -yaml.indent(mapping=2, sequence=2, offset=2) +yaml.indent(mapping=2, sequence=4, offset=2) yaml.encoding = "utf-8" yaml.allow_unicode = True --- aar_doc/defaults.py +++ aar_doc/defaults.py @@ -73,18 +73,27 @@ def add_default( else: self._defaults.setdefault(name, RoleDefault(name, value, description)) + def safe_quote_recursive(self, value): + if isinstance(value, list): + return [self.safe_quote_recursive(v) for v in value] + elif isinstance(value, dict): + return {k: self.safe_quote_recursive(v) for k, v in value.items()} + elif isinstance(value, str): + if value in ("yes", "no"): + return SingleQuotedScalarString(value) + elif "\n" in value: + return LiteralScalarString(value) + elif ":" in value: + return SingleQuotedScalarString(value) + return value + def to_commented_map(self) -> CommentedMap: """ Returns all tracked defaults as a CommentedMap. """ commented_defaults = CommentedMap() for role_default in self.defaults: - value = role_default.value - if isinstance(value, str): - if value in ("yes", "no"): - value = SingleQuotedScalarString(value) - if "\n" in value: - value = LiteralScalarString(value) + value = self.safe_quote_recursive(role_default.value) commented_defaults[role_default.name] = value description_items = ( role_default.description --- aar_doc/core.py +++ aar_doc/core.py @@ -7,6 +7,7 @@ and rendering jinja2 templates from processing data. import json import pathlib +import re from enum import Enum import jinja2 @@ -21,6 +22,24 @@ yaml.encoding = "utf-8" yaml.allow_unicode = True +def ansible_doc_markup(text): + # Regular expressions copied from ansible-doc: + # https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/doc.py#L436 + out = re.sub(r'\bI\(([^)]+)\)', r'*\1*', text) # I(text) -> *text* + out = re.sub(r'\bB\(([^)]+)\)', r'**\1**', out) # B(text) -> **text** + out = re.sub(r'\bC\(([^)]+)\)', r'`\1`', out) # C(text) -> `text` + out = re.sub(r'\bM\(([^)]+)\)', r'`\1`', out) # M(module) -> `module` + out = re.sub(r'\bO\(((?:[^\\)]+|\\.)+)\)', r'`\1`', out) # O(option) -> `option` + out = re.sub(r'\bV\(((?:[^\\)]+|\\.)+)\)', r'`\1`', out) # V(value) -> `value` + out = re.sub(r'\bV\(((?:[^\\)]+|\\.)+)\)', r'`\1`', out) # E(env) -> `env` + out = re.sub(r'\bV\(((?:[^\\)]+|\\.)+)\)', r'`\1`', out) # RV(retval) -> `retval` + out = re.sub(r'\bU\(([^)]+)\)', r'[\1]', out) # U(url) -> [url] + out = re.sub(r'\bL\(([^)]+), *([^)]+)\)', r'[\1](\2)', out) # L(text,url) -> [text](url) + out = re.sub(r'\bR\(([^)]+), *([^)]+)\)', r'[\1](#\2)', out) # R(text,frag) -> [text](#frag) + out = re.sub(r'\bHORIZONTALLINE\b', r'\n\n---\n', out) # HORIZONTALLINE -> --- + return out + + class OutputMode(Enum): """ Defines the options for the output mode. @@ -240,6 +259,7 @@ def render_content(ctx: typer.Context, content_template: str) -> str: autoescape=jinja2.select_autoescape(), undefined=jinja2.StrictUndefined, ) + env.filters['ansible_doc_markup'] = ansible_doc_markup role = ctx.obj["config"]["role"] metadata = ctx.obj["data"]["metadata"] @@ -270,12 +290,14 @@ def render_content(ctx: typer.Context, content_template: str) -> str: keep_trailing_newline=True, loader=jinja2.FileSystemLoader([role_path, output_template_file.parent]), ) + env.filters['ansible_doc_markup'] = ansible_doc_markup template = env.get_template(output_template_file.name) except (FileNotFoundError, OSError): env = jinja2.Environment( keep_trailing_newline=True, loader=jinja2.FileSystemLoader(role_path), ) + env.filters['ansible_doc_markup'] = ansible_doc_markup template = env.from_string(source=output_template) return template.render( --- aar_doc/templates/markdown.j2 +++ aar_doc/templates/markdown.j2 @@ -3,7 +3,7 @@ {%- if "version" in galaxy_collection %} Version: {{ galaxy_collection.version }} {% endif %} -{{ metadata.galaxy_info.description }} +{{ metadata.galaxy_info.description | ansible_doc_markup }} {% if ("galaxy_tags" in metadata.galaxy_info) and (metadata.galaxy_info.galaxy_tags | length > 0) %} Tags: {{ metadata.galaxy_info.galaxy_tags | join(', ') }} {%- endif %} @@ -22,14 +22,15 @@ Tags: {{ metadata.galaxy_info.galaxy_tags | join(', ') }} ### Entrypoint: {{ entrypoint }} -{{ argument_specs[entrypoint].short_description }} +{{ argument_specs[entrypoint].short_description | ansible_doc_markup }} {% if "description" in argument_specs[entrypoint] %} {%- if argument_specs[entrypoint].description is string -%} -{{ argument_specs[entrypoint].description }} +{{ argument_specs[entrypoint].description | ansible_doc_markup }} {% else %} {%- for line in argument_specs[entrypoint].description -%} -{{ line }} +{{ line | ansible_doc_markup }} + {% endfor -%} {% endif -%} {% endif -%} @@ -39,7 +40,7 @@ Tags: {{ metadata.galaxy_info.galaxy_tags | join(', ') }} |Option|Description|Type|Required|Default| |---|---|---|---|---| {%- for name, details in options.items() %} -| {{ name }} | {{ details.display_description }} | {{ details.display_type }} | {{ details.display_required }} | {{ details.display_default }} | +| {{ name }} | {{ details.display_description | ansible_doc_markup }} | {{ details.display_type }} | {{ details.display_required }} | {{ details.display_default }} | {%- endfor %} {% if entrypoint_options[entrypoint] | length > 1 -%} @@ -49,7 +50,7 @@ Tags: {{ metadata.galaxy_info.galaxy_tags | join(', ') }} |Option|Description|Type|Required|Default| |---|---|---|---|---| {%- for name, details in options.items() %} -| {{ name }} | {{ details.display_description }} | {{ details.display_type }} | {{ details.display_required }} | {{ details.display_default }} | +| {{ name }} | {{ details.display_description | ansible_doc_markup }} | {{ details.display_type }} | {{ details.display_required }} | {{ details.display_default }} | {%- endfor %} {% endfor -%}