"""HTML documentation formatter.""" import io from typing import TYPE_CHECKING, Any, Optional from cyclopts._markup import escape_html, extract_text if TYPE_CHECKING: from rich.console import Console, ConsoleOptions from cyclopts.help import HelpEntry, HelpPanel class HtmlFormatter: """HTML documentation formatter. Parameters ---------- heading_level : int Starting heading level for panels (default: 2). E.g., 2 produces "

Commands

", 3 produces "

Commands

". include_hidden : bool Include hidden commands/parameters in documentation (default: False). app_name : str The root application name for generating anchor IDs. command_chain : list[str] The current command chain for generating anchor IDs. """ def __init__( self, heading_level: int = 2, include_hidden: bool = False, app_name: str | None = None, command_chain: list[str] | None = None, ): self.heading_level = heading_level self.include_hidden = include_hidden self.app_name = app_name self.command_chain = command_chain or [] self._output = io.StringIO() def reset(self) -> None: """Reset the internal output buffer.""" self._output = io.StringIO() def get_output(self) -> str: """Get the accumulated HTML output. Returns ------- str The HTML documentation string. """ return self._output.getvalue() def __call__( self, console: Optional["Console"], options: Optional["ConsoleOptions"], panel: "HelpPanel", ) -> None: """Format and render a help panel as HTML. Parameters ---------- console : Optional[Console] Console for rendering (used for extracting plain text). options : Optional[ConsoleOptions] Console rendering options (unused for HTML). panel : HelpPanel Help panel to render. """ if not panel.entries: return # Write panel as a section self._output.write('
\n') # Write panel title as heading if panel.title: title_text = escape_html(extract_text(panel.title, console)) self._output.write(f'{title_text}\n') # Write panel description if present if panel.description: desc_text = escape_html(extract_text(panel.description, console)) if desc_text: self._output.write(f'
{desc_text}
\n') # Format entries based on panel type if panel.format == "command": self._format_command_panel(panel.entries, console) elif panel.format == "parameter": self._format_parameter_panel(panel.entries, console) self._output.write("
\n") def _format_command_panel(self, entries: list["HelpEntry"], console: Optional["Console"]) -> None: """Format command entries as HTML. Parameters ---------- entries : list[HelpEntry] Command entries to format. console : Optional[Console] Console for text extraction. """ if not entries: return # Use list format instead of table self._output.write('\n") def _format_parameter_panel(self, entries: list["HelpEntry"], console: Optional["Console"]) -> None: """Format parameter entries as HTML. Parameters ---------- entries : list[HelpEntry] Parameter entries to format. console : Optional[Console] Console for text extraction. """ if not entries: return # Use list format instead of table self._output.write('\n") def render_usage( self, console: Optional["Console"], options: Optional["ConsoleOptions"], usage: Any, ) -> None: """Render the usage line as HTML. Parameters ---------- console : Optional[Console] Console for text extraction. options : Optional[ConsoleOptions] Console rendering options (unused). usage : Any The usage line content. """ if usage: usage_text = escape_html(extract_text(usage, console)) if usage_text: self._output.write('
\n') # Add "Usage:" prefix if not already present (for custom usage strings) if not usage_text.strip().startswith("Usage:"): self._output.write(f'
Usage: {usage_text}
\n') else: self._output.write(f'
{usage_text}
\n') self._output.write("
\n") def render_description( self, console: Optional["Console"], options: Optional["ConsoleOptions"], description: Any, ) -> None: """Render the description as HTML. Parameters ---------- console : Optional[Console] Console for text extraction. options : Optional[ConsoleOptions] Console rendering options (unused). description : Any The description content. """ if description: desc_text = escape_html(extract_text(description, console)) if desc_text: self._output.write(f'
{desc_text}
\n')