Files
2024-10-30 22:14:35 +01:00

127 lines
4.1 KiB
Python

from typing import Iterable, List, Optional, Sequence
from .compat import Literal
class MarkdownRenderer:
"""Simple helper for generating raw Markdown."""
def __init__(self, no_emoji: bool = False):
"""Initialize the renderer.
no_emoji (bool): Don't show emoji in titles etc.
"""
self.data: List = []
self.no_emoji = no_emoji
@property
def text(self) -> str:
"""RETURNS (str): The Markdown document."""
return "\n\n".join(self.data)
def add(self, content: str):
"""Add a string to the Markdown document.
content (str): Add content to the document.
"""
self.data.append(content)
def table(
self,
data: Iterable[Iterable[str]],
header: Sequence[str],
aligns: Optional[Sequence[Literal["r", "c", "l"]]] = None,
) -> str:
"""Create a Markdown table.
data (Iterable[Iterable[str]]): The body, one iterable per row,
containig an interable of column contents.
header (Sequence[str]): The column names.
aligns (Optional[Sequence[Literal["r", "c", "l"]]]): Optional alignment-mode for each column. Values should
either be 'l' (left), 'r' (right), or 'c' (center). Optional.
RETURNS (str): The rendered table.
"""
if aligns is None:
aligns = ["l"] * len(header)
if len(aligns) != len(header):
err = "Invalid aligns: {} (header length: {})".format(aligns, len(header))
raise ValueError(err)
get_divider = lambda a: ":---:" if a == "c" else "---:" if a == "r" else "---"
head = "| {} |".format(" | ".join(header))
divider = "| {} |".format(
" | ".join(get_divider(aligns[i]) for i in range(len(header)))
)
body = "\n".join("| {} |".format(" | ".join(row)) for row in data)
return "{}\n{}\n{}".format(head, divider, body)
def title(self, level: int, text: str, emoji: Optional[str] = None) -> str:
"""Create a Markdown heading.
level (int): The heading level, e.g. 3 for ###
text (str): The heading text.
emoji (Optional[str]): Optional emoji to show before heading text, if enabled.
RETURNS (str): The rendered title.
"""
prefix = "{} ".format(emoji) if emoji and not self.no_emoji else ""
return "{} {}{}".format("#" * level, prefix, text)
def list(self, items: Iterable[str], numbered: bool = False) -> str:
"""Create a non-nested list.
items (Iterable[str]): The list items.
numbered (bool): Whether to use a numbered list.
RETURNS (str): The rendered list.
"""
content = []
for i, item in enumerate(items):
if numbered:
content.append("{}. {}".format(i + 1, item))
else:
content.append("- {}".format(item))
return "\n".join(content)
def link(self, text: str, url: str) -> str:
"""Create a Markdown link.
text (str): The link text.
url (str): The link URL.
RETURNS (str): The rendered link.
"""
return "[{}]({})".format(text, url)
def code_block(self, text: str, lang: str = "") -> str:
"""Create a Markdown code block.
text (str): The code text.
lang (str): Optional code language.
RETURNS (str): The rendered code block.
"""
return "```{}\n{}\n```".format(lang, text)
def code(self, text: str) -> str:
"""Create Markdown inline code.
text (str): The inline code text.
RETURNS (str): The rendered code text.
"""
return self._wrap(text, "`")
def bold(self, text: str) -> str:
"""Create bold text.
text (str): The text to format in boldface.
RETURNS (str): The formatted text.
"""
return self._wrap(text, "**")
def italic(self, text: str):
"""Create italic text.
text (str): The text to italicize.
RETURNS (str): The formatted text.
"""
return self._wrap(text, "_")
def _wrap(self, text, marker):
return "{}{}{}".format(marker, text, marker)