From 6d7ba58f880be618ade07f8ea080fe8c4bf8a896 Mon Sep 17 00:00:00 2001 From: cyfraeviolae Date: Wed, 3 Apr 2024 03:10:44 -0400 Subject: venv --- .../site-packages/markdown_it/renderer.py | 336 +++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 venv/lib/python3.11/site-packages/markdown_it/renderer.py (limited to 'venv/lib/python3.11/site-packages/markdown_it/renderer.py') diff --git a/venv/lib/python3.11/site-packages/markdown_it/renderer.py b/venv/lib/python3.11/site-packages/markdown_it/renderer.py new file mode 100644 index 0000000..7fee9ff --- /dev/null +++ b/venv/lib/python3.11/site-packages/markdown_it/renderer.py @@ -0,0 +1,336 @@ +""" +class Renderer + +Generates HTML from parsed token stream. Each instance has independent +copy of rules. Those can be rewritten with ease. Also, you can add new +rules if you create plugin and adds new token types. +""" +from __future__ import annotations + +from collections.abc import Sequence +import inspect +from typing import Any, ClassVar, Protocol + +from .common.utils import escapeHtml, unescapeAll +from .token import Token +from .utils import EnvType, OptionsDict + + +class RendererProtocol(Protocol): + __output__: ClassVar[str] + + def render( + self, tokens: Sequence[Token], options: OptionsDict, env: EnvType + ) -> Any: + ... + + +class RendererHTML(RendererProtocol): + """Contains render rules for tokens. Can be updated and extended. + + Example: + + Each rule is called as independent static function with fixed signature: + + :: + + class Renderer: + def token_type_name(self, tokens, idx, options, env) { + # ... + return renderedHTML + + :: + + class CustomRenderer(RendererHTML): + def strong_open(self, tokens, idx, options, env): + return '' + def strong_close(self, tokens, idx, options, env): + return '' + + md = MarkdownIt(renderer_cls=CustomRenderer) + + result = md.render(...) + + See https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.js + for more details and examples. + """ + + __output__ = "html" + + def __init__(self, parser: Any = None): + self.rules = { + k: v + for k, v in inspect.getmembers(self, predicate=inspect.ismethod) + if not (k.startswith("render") or k.startswith("_")) + } + + def render( + self, tokens: Sequence[Token], options: OptionsDict, env: EnvType + ) -> str: + """Takes token stream and generates HTML. + + :param tokens: list on block tokens to render + :param options: params of parser instance + :param env: additional data from parsed input + + """ + result = "" + + for i, token in enumerate(tokens): + if token.type == "inline": + if token.children: + result += self.renderInline(token.children, options, env) + elif token.type in self.rules: + result += self.rules[token.type](tokens, i, options, env) + else: + result += self.renderToken(tokens, i, options, env) + + return result + + def renderInline( + self, tokens: Sequence[Token], options: OptionsDict, env: EnvType + ) -> str: + """The same as ``render``, but for single token of `inline` type. + + :param tokens: list on block tokens to render + :param options: params of parser instance + :param env: additional data from parsed input (references, for example) + """ + result = "" + + for i, token in enumerate(tokens): + if token.type in self.rules: + result += self.rules[token.type](tokens, i, options, env) + else: + result += self.renderToken(tokens, i, options, env) + + return result + + def renderToken( + self, + tokens: Sequence[Token], + idx: int, + options: OptionsDict, + env: EnvType, + ) -> str: + """Default token renderer. + + Can be overridden by custom function + + :param idx: token index to render + :param options: params of parser instance + """ + result = "" + needLf = False + token = tokens[idx] + + # Tight list paragraphs + if token.hidden: + return "" + + # Insert a newline between hidden paragraph and subsequent opening + # block-level tag. + # + # For example, here we should insert a newline before blockquote: + # - a + # > + # + if token.block and token.nesting != -1 and idx and tokens[idx - 1].hidden: + result += "\n" + + # Add token name, e.g. ``. + # + needLf = False + + result += ">\n" if needLf else ">" + + return result + + @staticmethod + def renderAttrs(token: Token) -> str: + """Render token attributes to string.""" + result = "" + + for key, value in token.attrItems(): + result += " " + escapeHtml(key) + '="' + escapeHtml(str(value)) + '"' + + return result + + def renderInlineAsText( + self, + tokens: Sequence[Token] | None, + options: OptionsDict, + env: EnvType, + ) -> str: + """Special kludge for image `alt` attributes to conform CommonMark spec. + + Don't try to use it! Spec requires to show `alt` content with stripped markup, + instead of simple escaping. + + :param tokens: list on block tokens to render + :param options: params of parser instance + :param env: additional data from parsed input + """ + result = "" + + for token in tokens or []: + if token.type == "text": + result += token.content + elif token.type == "image": + if token.children: + result += self.renderInlineAsText(token.children, options, env) + elif token.type == "softbreak": + result += "\n" + + return result + + ################################################### + + def code_inline( + self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType + ) -> str: + token = tokens[idx] + return ( + "" + + escapeHtml(tokens[idx].content) + + "" + ) + + def code_block( + self, + tokens: Sequence[Token], + idx: int, + options: OptionsDict, + env: EnvType, + ) -> str: + token = tokens[idx] + + return ( + "" + + escapeHtml(tokens[idx].content) + + "\n" + ) + + def fence( + self, + tokens: Sequence[Token], + idx: int, + options: OptionsDict, + env: EnvType, + ) -> str: + token = tokens[idx] + info = unescapeAll(token.info).strip() if token.info else "" + langName = "" + langAttrs = "" + + if info: + arr = info.split(maxsplit=1) + langName = arr[0] + if len(arr) == 2: + langAttrs = arr[1] + + if options.highlight: + highlighted = options.highlight( + token.content, langName, langAttrs + ) or escapeHtml(token.content) + else: + highlighted = escapeHtml(token.content) + + if highlighted.startswith("" + + highlighted + + "\n" + ) + + return ( + "
"
+            + highlighted
+            + "
\n" + ) + + def image( + self, + tokens: Sequence[Token], + idx: int, + options: OptionsDict, + env: EnvType, + ) -> str: + token = tokens[idx] + + # "alt" attr MUST be set, even if empty. Because it's mandatory and + # should be placed on proper position for tests. + if token.children: + token.attrSet("alt", self.renderInlineAsText(token.children, options, env)) + else: + token.attrSet("alt", "") + + return self.renderToken(tokens, idx, options, env) + + def hardbreak( + self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType + ) -> str: + return "
\n" if options.xhtmlOut else "
\n" + + def softbreak( + self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType + ) -> str: + return ( + ("
\n" if options.xhtmlOut else "
\n") if options.breaks else "\n" + ) + + def text( + self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType + ) -> str: + return escapeHtml(tokens[idx].content) + + def html_block( + self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType + ) -> str: + return tokens[idx].content + + def html_inline( + self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType + ) -> str: + return tokens[idx].content -- cgit v1.2.3