Add generators: man, markdown, and HTML generators with validators
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
2026-01-31 00:57:31 +00:00
parent d937163e92
commit ac7f44005c

View File

@@ -0,0 +1,134 @@
"""Markdown generator for Doc2Man."""
from pathlib import Path
from typing import Any, Dict, List, Optional
from jinja2 import Environment, FileSystemLoader, TemplateSyntaxError
def generate_markdown(
parsed_data: List[Dict[str, Any]],
output_path: Path,
template_path: Optional[Path] = None,
) -> str:
"""Generate Markdown documentation from parsed data.
Args:
parsed_data: List of parsed documentation dictionaries.
output_path: Path to write the markdown file.
template_path: Optional custom template file.
Returns:
The generated Markdown content.
"""
env = Environment(
loader=FileSystemLoader(str(template_path.parent) if template_path else get_template_dir()),
autoescape=True,
)
env.filters['first_line'] = first_line_filter
try:
if template_path:
template = env.from_string(template_path.read_text())
else:
template = env.get_template("markdown.j2")
except TemplateSyntaxError as e:
raise ValueError(f"Template syntax error: {e}")
md_content = template.render(
data=parsed_data,
title=get_md_title(parsed_data),
)
if output_path:
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(md_content, encoding="utf-8")
return md_content
def first_line_filter(text: str) -> str:
"""Get the first line of text."""
if not text:
return ""
lines = text.split('\n')
return lines[0] if lines else ""
def get_template_dir() -> Path:
"""Get the directory containing templates."""
return Path(__file__).parent.parent / "templates"
def get_md_title(parsed_data: List[Dict[str, Any]]) -> str:
"""Extract a title for the Markdown document."""
for item in parsed_data:
data = item.get("data", {})
if data.get("title"):
return data["title"]
if data.get("functions"):
for func in data["functions"]:
if func.get("name"):
return func["name"]
return "Documentation"
class MarkdownValidator:
"""Validator for Markdown format."""
VALID_HEADERS = {"#", "##", "###", "####", "#####", "######"}
@staticmethod
def validate(content: str) -> List[str]:
"""Validate Markdown content.
Args:
content: The Markdown content to validate.
Returns:
List of validation warnings.
"""
warnings = []
lines = content.split("\n")
has_header = False
for line in lines:
stripped = line.lstrip()
if stripped.startswith("#"):
has_header = True
break
if not has_header:
warnings.append("No markdown header found (document should start with #)")
return warnings
@staticmethod
def validate_code_blocks(content: str) -> List[str]:
"""Validate code blocks in Markdown.
Args:
content: The Markdown content to validate.
Returns:
List of validation errors.
"""
errors = []
lines = content.split("\n")
in_code_block = False
code_language = None
for i, line in enumerate(lines):
if line.strip().startswith("```"):
if not in_code_block:
in_code_block = True
code_language = line.strip()[3:].strip() or None
else:
in_code_block = False
code_language = None
elif in_code_block and line.strip() and not line.startswith(" ") and not line.startswith("\t"):
if code_language is None:
errors.append(f"Line {i+1}: Code block content should be indented or language should be specified")
return errors