summaryrefslogtreecommitdiff
path: root/src/mistune/markdown.py
blob: c814a59ff977abbb2c6c13379a46401823337729 (plain) (blame)
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
from typing import Optional
from .core import BlockState
from .block_parser import BlockParser
from .inline_parser import InlineParser


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=None, block=None, inline=None, plugins=None):
        if block is None:
            block = BlockParser()

        if inline is None:
            inline = InlineParser()

        self.renderer = renderer
        self.block = block
        self.inline = inline
        self.before_parse_hooks = []
        self.before_render_hooks = []
        self.after_render_hooks = []

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

    def use(self, plugin):
        plugin(self)

    def render_state(self, state: BlockState):
        data = self._iter_render(state.tokens, state)
        if self.renderer:
            return self.renderer(data, state)
        return list(data)

    def _iter_render(self, tokens, state):
        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
                tok['children'] = self.inline(text.strip(), state.env)
            yield tok

    def parse(self, s: str, state: Optional[BlockState]=None):
        """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')

        state.process(s)

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

        self.block.parse(state)

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

        result = self.render_state(state)

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

    def read(self, filepath, encoding='utf-8', state=None):
        if state is None:
            state = self.block.state_cls()

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

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

    def __call__(self, s: str):
        if s is None:
            s = '\n'
        return self.parse(s)[0]