summaryrefslogtreecommitdiff
path: root/src/mistune/plugins/ruby.py
blob: eabc037f53ee3223dccba4d5d45752765ce8fa52 (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
import re
from ..util import unikey
from ..helpers import parse_link, parse_link_label


RUBY_PATTERN = r'\[(?:\w+\(\w+\))+\]'
_ruby_re = re.compile(RUBY_PATTERN)


def parse_ruby(inline, m, state):
    text = m.group(0)[1:-2]
    items = text.split(')')
    tokens = []
    for item in items:
        rb, rt = item.split('(')
        tokens.append({
            'type': 'ruby',
            'raw': rb,
            'attrs': {'rt': rt}
        })

    end_pos = m.end()

    next_match = _ruby_re.match(state.src, end_pos)
    if next_match:
        for tok in tokens:
            state.append_token(tok)
        return parse_ruby(inline, next_match, state)

    # repeat link logic
    if end_pos < len(state.src):
        link_pos = _parse_ruby_link(inline, state, end_pos, tokens)
        if link_pos:
            return link_pos

    for tok in tokens:
        state.append_token(tok)
    return end_pos


def _parse_ruby_link(inline, state, pos, tokens):
    c = state.src[pos]
    if c == '(':
        # standard link [text](<url> "title")
        attrs, link_pos = parse_link(state.src, pos + 1)
        if link_pos:
            state.append_token({
                'type': 'link',
                'children': tokens,
                'attrs': attrs,
            })
            return link_pos

    elif c == '[':
        # standard ref link [text][label]
        label, link_pos = parse_link_label(state.src, pos + 1)
        if label and link_pos:
            ref_links = state.env['ref_links']
            key = unikey(label)
            env = ref_links.get(key)
            if env:
                attrs = {'url': env['url'], 'title': env.get('title')}
                state.append_token({
                    'type': 'link',
                    'children': tokens,
                    'attrs': attrs,
                })
            else:
                for tok in tokens:
                    state.append_token(tok)
                state.append_token({
                    'type': 'text',
                    'raw': '[' + label + ']',
                })
            return link_pos


def render_ruby(renderer, text, rt):
    return '<ruby><rb>' + text + '</rb><rt>' + rt + '</rt></ruby>'


def ruby(md):
    """A mistune plugin to support ``<ruby>`` tag. The syntax is defined
    at https://lepture.com/en/2022/markdown-ruby-markup:

    .. code-block:: text

        [漢字(ㄏㄢˋㄗˋ)]
        [漢(ㄏㄢˋ)字(ㄗˋ)]

        [漢字(ㄏㄢˋㄗˋ)][link]
        [漢字(ㄏㄢˋㄗˋ)](/url "title")

        [link]: /url "title"

    :param md: Markdown instance
    """
    md.inline.register('ruby', RUBY_PATTERN, parse_ruby, before='link')
    if md.renderer and md.renderer.NAME == 'html':
        md.renderer.register('ruby', render_ruby)