File size: 4,019 Bytes
5f5a27c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union

from .block_parser import BlockParser
from .core import BaseRenderer, BlockState
from .inline_parser import InlineParser
from .plugins import Plugin


class Markdown:
    """Markdown instance to convert markdown text into HTML or other formats.
    Here is an example with the HTMLRenderer::

        from mistune import HTMLRenderer

        md = Markdown(renderer=HTMLRenderer(escape=False))
        md('hello **world**')

    :param renderer: a renderer to convert parsed tokens
    :param block: block level syntax parser
    :param inline: inline level syntax parser
    :param plugins: mistune plugins to use
    """

    def __init__(
        self,
        renderer: Optional[BaseRenderer] = None,
        block: Optional[BlockParser] = None,
        inline: Optional[InlineParser] = None,
        plugins: Optional[Iterable[Plugin]] = None,
    ):
        if block is None:
            block = BlockParser()

        if inline is None:
            inline = InlineParser()

        self.renderer = renderer
        self.block: BlockParser = block
        self.inline: InlineParser = inline
        self.before_parse_hooks: List[Callable[["Markdown", BlockState], None]] = []
        self.before_render_hooks: List[Callable[["Markdown", BlockState], Any]] = []
        self.after_render_hooks: List[
            Callable[["Markdown", Union[str, List[Dict[str, Any]]], BlockState], Union[str, List[Dict[str, Any]]]]
        ] = []

        if plugins:
            for plugin in plugins:
                plugin(self)

    def use(self, plugin: Plugin) -> None:
        plugin(self)

    def render_state(self, state: BlockState) -> Union[str, List[Dict[str, Any]]]:
        data = self._iter_render(state.tokens, state)
        if self.renderer:
            return self.renderer(data, state)
        return list(data)

    def _iter_render(self, tokens: Iterable[Dict[str, Any]], state: BlockState) -> Iterable[Dict[str, Any]]:
        for tok in tokens:
            if "children" in tok:
                children = self._iter_render(tok["children"], state)
                tok["children"] = list(children)
            elif "text" in tok:
                text = tok.pop("text")
                # process inline text
                # avoid striping emsp or other unicode spaces
                tok["children"] = self.inline(text.strip(" \r\n\t\f"), state.env)
            yield tok

    def parse(self, s: str, state: Optional[BlockState] = None) -> Tuple[Union[str, List[Dict[str, Any]]], BlockState]:
        """Parse and convert the given markdown string. If renderer is None,
        the returned **result** will be parsed markdown tokens.

        :param s: markdown string
        :param state: instance of BlockState
        :returns: result, state
        """
        if state is None:
            state = self.block.state_cls()

        # normalize line separator
        s = s.replace("\r\n", "\n")
        s = s.replace("\r", "\n")
        if not s.endswith("\n"):
            s += "\n"

        state.process(s)

        for hook in self.before_parse_hooks:
            hook(self, state)

        self.block.parse(state)

        for hook2 in self.before_render_hooks:
            hook2(self, state)

        result = self.render_state(state)

        for hook3 in self.after_render_hooks:
            result = hook3(self, result, state)
        return result, state

    def read(
        self, filepath: str, encoding: str = "utf-8", state: Optional[BlockState] = None
    ) -> Tuple[Union[str, List[Dict[str, Any]]], BlockState]:
        if state is None:
            state = self.block.state_cls()

        state.env["__file__"] = filepath
        with open(filepath, "rb") as f:
            s = f.read()

        s2 = s.decode(encoding)
        return self.parse(s2, state)

    def __call__(self, s: str) -> Union[str, List[Dict[str, Any]]]:
        if s is None:
            s = "\n"
        return self.parse(s)[0]