| @@ 916-1128 (lines=213) @@ | ||
| 913 | return html % (self.hrule(), text) |
|
| 914 | ||
| 915 | ||
| 916 | class Markdown(object): |
|
| 917 | """The Markdown parser. |
|
| 918 | ||
| 919 | :param renderer: An instance of ``Renderer``. |
|
| 920 | :param inline: An inline lexer class or instance. |
|
| 921 | :param block: A block lexer class or instance. |
|
| 922 | """ |
|
| 923 | def __init__(self, renderer=None, inline=None, block=None, **kwargs): |
|
| 924 | if not renderer: |
|
| 925 | renderer = Renderer(**kwargs) |
|
| 926 | ||
| 927 | self.renderer = renderer |
|
| 928 | ||
| 929 | if inline and inspect.isclass(inline): |
|
| 930 | inline = inline(renderer, **kwargs) |
|
| 931 | if block and inspect.isclass(block): |
|
| 932 | block = block(**kwargs) |
|
| 933 | ||
| 934 | if inline: |
|
| 935 | self.inline = inline |
|
| 936 | else: |
|
| 937 | rules = InlineGrammar() |
|
| 938 | if kwargs.get('hard_wrap'): |
|
| 939 | rules.hard_wrap() |
|
| 940 | self.inline = InlineLexer(renderer, rules=rules) |
|
| 941 | ||
| 942 | self.block = block or BlockLexer(BlockGrammar()) |
|
| 943 | self.options = kwargs |
|
| 944 | self.footnotes = [] |
|
| 945 | self.tokens = [] |
|
| 946 | ||
| 947 | # detect if it should parse text in block html |
|
| 948 | self._parse_block_html = kwargs.get('parse_block_html') |
|
| 949 | ||
| 950 | def __call__(self, text): |
|
| 951 | return self.parse(text) |
|
| 952 | ||
| 953 | def render(self, text): |
|
| 954 | """Render the Markdown text. |
|
| 955 | ||
| 956 | :param text: markdown formatted text content. |
|
| 957 | """ |
|
| 958 | return self.parse(text) |
|
| 959 | ||
| 960 | def parse(self, text): |
|
| 961 | out = self.output(preprocessing(text)) |
|
| 962 | ||
| 963 | keys = self.block.def_footnotes |
|
| 964 | ||
| 965 | # reset block |
|
| 966 | self.block.def_links = {} |
|
| 967 | self.block.def_footnotes = {} |
|
| 968 | ||
| 969 | # reset inline |
|
| 970 | self.inline.links = {} |
|
| 971 | self.inline.footnotes = {} |
|
| 972 | ||
| 973 | if not self.footnotes: |
|
| 974 | return out |
|
| 975 | ||
| 976 | footnotes = filter(lambda o: keys.get(o['key']), self.footnotes) |
|
| 977 | self.footnotes = sorted( |
|
| 978 | footnotes, key=lambda o: keys.get(o['key']), reverse=True |
|
| 979 | ) |
|
| 980 | ||
| 981 | body = self.renderer.placeholder() |
|
| 982 | while self.footnotes: |
|
| 983 | note = self.footnotes.pop() |
|
| 984 | body += self.renderer.footnote_item( |
|
| 985 | note['key'], note['text'] |
|
| 986 | ) |
|
| 987 | ||
| 988 | out += self.renderer.footnotes(body) |
|
| 989 | return out |
|
| 990 | ||
| 991 | def pop(self): |
|
| 992 | if not self.tokens: |
|
| 993 | return None |
|
| 994 | self.token = self.tokens.pop() |
|
| 995 | return self.token |
|
| 996 | ||
| 997 | def peek(self): |
|
| 998 | if self.tokens: |
|
| 999 | return self.tokens[-1] |
|
| 1000 | return None # pragma: no cover |
|
| 1001 | ||
| 1002 | def output(self, text, rules=None): |
|
| 1003 | self.tokens = self.block(text, rules) |
|
| 1004 | self.tokens.reverse() |
|
| 1005 | ||
| 1006 | self.inline.setup(self.block.def_links, self.block.def_footnotes) |
|
| 1007 | ||
| 1008 | out = self.renderer.placeholder() |
|
| 1009 | while self.pop(): |
|
| 1010 | out += self.tok() |
|
| 1011 | return out |
|
| 1012 | ||
| 1013 | def tok(self): |
|
| 1014 | t = self.token['type'] |
|
| 1015 | ||
| 1016 | # sepcial cases |
|
| 1017 | if t.endswith('_start'): |
|
| 1018 | t = t[:-6] |
|
| 1019 | ||
| 1020 | return getattr(self, 'output_%s' % t)() |
|
| 1021 | ||
| 1022 | def tok_text(self): |
|
| 1023 | text = self.token['text'] |
|
| 1024 | while self.peek()['type'] == 'text': |
|
| 1025 | text += '\n' + self.pop()['text'] |
|
| 1026 | return self.inline(text) |
|
| 1027 | ||
| 1028 | def output_newline(self): |
|
| 1029 | return self.renderer.newline() |
|
| 1030 | ||
| 1031 | def output_hrule(self): |
|
| 1032 | return self.renderer.hrule() |
|
| 1033 | ||
| 1034 | def output_heading(self): |
|
| 1035 | return self.renderer.header( |
|
| 1036 | self.inline(self.token['text']), |
|
| 1037 | self.token['level'], |
|
| 1038 | self.token['text'], |
|
| 1039 | ) |
|
| 1040 | ||
| 1041 | def output_code(self): |
|
| 1042 | return self.renderer.block_code( |
|
| 1043 | self.token['text'], self.token['lang'] |
|
| 1044 | ) |
|
| 1045 | ||
| 1046 | def output_table(self): |
|
| 1047 | aligns = self.token['align'] |
|
| 1048 | aligns_length = len(aligns) |
|
| 1049 | cell = self.renderer.placeholder() |
|
| 1050 | ||
| 1051 | # header part |
|
| 1052 | header = self.renderer.placeholder() |
|
| 1053 | for i, value in enumerate(self.token['header']): |
|
| 1054 | align = aligns[i] if i < aligns_length else None |
|
| 1055 | flags = {'header': True, 'align': align} |
|
| 1056 | cell += self.renderer.table_cell(self.inline(value), **flags) |
|
| 1057 | ||
| 1058 | header += self.renderer.table_row(cell) |
|
| 1059 | ||
| 1060 | # body part |
|
| 1061 | body = self.renderer.placeholder() |
|
| 1062 | for i, row in enumerate(self.token['cells']): |
|
| 1063 | cell = self.renderer.placeholder() |
|
| 1064 | for j, value in enumerate(row): |
|
| 1065 | align = aligns[j] if j < aligns_length else None |
|
| 1066 | flags = {'header': False, 'align': align} |
|
| 1067 | cell += self.renderer.table_cell(self.inline(value), **flags) |
|
| 1068 | body += self.renderer.table_row(cell) |
|
| 1069 | ||
| 1070 | return self.renderer.table(header, body) |
|
| 1071 | ||
| 1072 | def output_block_quote(self): |
|
| 1073 | body = self.renderer.placeholder() |
|
| 1074 | while self.pop()['type'] != 'block_quote_end': |
|
| 1075 | body += self.tok() |
|
| 1076 | return self.renderer.block_quote(body) |
|
| 1077 | ||
| 1078 | def output_list(self): |
|
| 1079 | ordered = self.token['ordered'] |
|
| 1080 | body = self.renderer.placeholder() |
|
| 1081 | while self.pop()['type'] != 'list_end': |
|
| 1082 | body += self.tok() |
|
| 1083 | return self.renderer.list(body, ordered) |
|
| 1084 | ||
| 1085 | def output_list_item(self): |
|
| 1086 | body = self.renderer.placeholder() |
|
| 1087 | while self.pop()['type'] != 'list_item_end': |
|
| 1088 | if self.token['type'] == 'text': |
|
| 1089 | body += self.tok_text() |
|
| 1090 | else: |
|
| 1091 | body += self.tok() |
|
| 1092 | ||
| 1093 | return self.renderer.list_item(body) |
|
| 1094 | ||
| 1095 | def output_loose_item(self): |
|
| 1096 | body = self.renderer.placeholder() |
|
| 1097 | while self.pop()['type'] != 'list_item_end': |
|
| 1098 | body += self.tok() |
|
| 1099 | return self.renderer.list_item(body) |
|
| 1100 | ||
| 1101 | def output_footnote(self): |
|
| 1102 | self.inline._in_footnote = True |
|
| 1103 | body = self.renderer.placeholder() |
|
| 1104 | key = self.token['key'] |
|
| 1105 | while self.pop()['type'] != 'footnote_end': |
|
| 1106 | body += self.tok() |
|
| 1107 | self.footnotes.append({'key': key, 'text': body}) |
|
| 1108 | self.inline._in_footnote = False |
|
| 1109 | return self.renderer.placeholder() |
|
| 1110 | ||
| 1111 | def output_close_html(self): |
|
| 1112 | text = self.token['text'] |
|
| 1113 | return self.renderer.block_html(text) |
|
| 1114 | ||
| 1115 | def output_open_html(self): |
|
| 1116 | text = self.token['text'] |
|
| 1117 | tag = self.token['tag'] |
|
| 1118 | if self._parse_block_html and tag not in _pre_tags: |
|
| 1119 | text = self.inline(text, rules=self.inline.inline_html_rules) |
|
| 1120 | extra = self.token.get('extra') or '' |
|
| 1121 | html = '<%s%s>%s</%s>' % (tag, extra, text, tag) |
|
| 1122 | return self.renderer.block_html(html) |
|
| 1123 | ||
| 1124 | def output_paragraph(self): |
|
| 1125 | return self.renderer.paragraph(self.inline(self.token['text'])) |
|
| 1126 | ||
| 1127 | def output_text(self): |
|
| 1128 | return self.renderer.paragraph(self.tok_text()) |
|
| 1129 | ||
| 1130 | ||
| 1131 | def markdown(text, escape=True, **kwargs): |
|
| @@ 946-1156 (lines=211) @@ | ||
| 943 | return html % (self.hrule(), text) |
|
| 944 | ||
| 945 | ||
| 946 | class Markdown(object): |
|
| 947 | """The Markdown parser. |
|
| 948 | ||
| 949 | :param renderer: An instance of ``Renderer``. |
|
| 950 | :param inline: An inline lexer class or instance. |
|
| 951 | :param block: A block lexer class or instance. |
|
| 952 | """ |
|
| 953 | def __init__(self, renderer=None, inline=None, block=None, **kwargs): |
|
| 954 | if not renderer: |
|
| 955 | renderer = Renderer(**kwargs) |
|
| 956 | else: |
|
| 957 | kwargs.update(renderer.options) |
|
| 958 | ||
| 959 | self.renderer = renderer |
|
| 960 | ||
| 961 | if inline and inspect.isclass(inline): |
|
| 962 | inline = inline(renderer, **kwargs) |
|
| 963 | if block and inspect.isclass(block): |
|
| 964 | block = block(**kwargs) |
|
| 965 | ||
| 966 | if inline: |
|
| 967 | self.inline = inline |
|
| 968 | else: |
|
| 969 | self.inline = InlineLexer(renderer, **kwargs) |
|
| 970 | ||
| 971 | self.block = block or BlockLexer(BlockGrammar()) |
|
| 972 | self.footnotes = [] |
|
| 973 | self.tokens = [] |
|
| 974 | ||
| 975 | # detect if it should parse text in block html |
|
| 976 | self._parse_block_html = kwargs.get('parse_block_html') |
|
| 977 | ||
| 978 | def __call__(self, text): |
|
| 979 | return self.parse(text) |
|
| 980 | ||
| 981 | def render(self, text): |
|
| 982 | """Render the Markdown text. |
|
| 983 | ||
| 984 | :param text: markdown formatted text content. |
|
| 985 | """ |
|
| 986 | return self.parse(text) |
|
| 987 | ||
| 988 | def parse(self, text): |
|
| 989 | out = self.output(preprocessing(text)) |
|
| 990 | ||
| 991 | keys = self.block.def_footnotes |
|
| 992 | ||
| 993 | # reset block |
|
| 994 | self.block.def_links = {} |
|
| 995 | self.block.def_footnotes = {} |
|
| 996 | ||
| 997 | # reset inline |
|
| 998 | self.inline.links = {} |
|
| 999 | self.inline.footnotes = {} |
|
| 1000 | ||
| 1001 | if not self.footnotes: |
|
| 1002 | return out |
|
| 1003 | ||
| 1004 | footnotes = filter(lambda o: keys.get(o['key']), self.footnotes) |
|
| 1005 | self.footnotes = sorted( |
|
| 1006 | footnotes, key=lambda o: keys.get(o['key']), reverse=True |
|
| 1007 | ) |
|
| 1008 | ||
| 1009 | body = self.renderer.placeholder() |
|
| 1010 | while self.footnotes: |
|
| 1011 | note = self.footnotes.pop() |
|
| 1012 | body += self.renderer.footnote_item( |
|
| 1013 | note['key'], note['text'] |
|
| 1014 | ) |
|
| 1015 | ||
| 1016 | out += self.renderer.footnotes(body) |
|
| 1017 | return out |
|
| 1018 | ||
| 1019 | def pop(self): |
|
| 1020 | if not self.tokens: |
|
| 1021 | return None |
|
| 1022 | self.token = self.tokens.pop() |
|
| 1023 | return self.token |
|
| 1024 | ||
| 1025 | def peek(self): |
|
| 1026 | if self.tokens: |
|
| 1027 | return self.tokens[-1] |
|
| 1028 | return None # pragma: no cover |
|
| 1029 | ||
| 1030 | def output(self, text, rules=None): |
|
| 1031 | self.tokens = self.block(text, rules) |
|
| 1032 | self.tokens.reverse() |
|
| 1033 | ||
| 1034 | self.inline.setup(self.block.def_links, self.block.def_footnotes) |
|
| 1035 | ||
| 1036 | out = self.renderer.placeholder() |
|
| 1037 | while self.pop(): |
|
| 1038 | out += self.tok() |
|
| 1039 | return out |
|
| 1040 | ||
| 1041 | def tok(self): |
|
| 1042 | t = self.token['type'] |
|
| 1043 | ||
| 1044 | # sepcial cases |
|
| 1045 | if t.endswith('_start'): |
|
| 1046 | t = t[:-6] |
|
| 1047 | ||
| 1048 | return getattr(self, 'output_%s' % t)() |
|
| 1049 | ||
| 1050 | def tok_text(self): |
|
| 1051 | text = self.token['text'] |
|
| 1052 | while self.peek()['type'] == 'text': |
|
| 1053 | text += '\n' + self.pop()['text'] |
|
| 1054 | return self.inline(text) |
|
| 1055 | ||
| 1056 | def output_newline(self): |
|
| 1057 | return self.renderer.newline() |
|
| 1058 | ||
| 1059 | def output_hrule(self): |
|
| 1060 | return self.renderer.hrule() |
|
| 1061 | ||
| 1062 | def output_heading(self): |
|
| 1063 | return self.renderer.header( |
|
| 1064 | self.inline(self.token['text']), |
|
| 1065 | self.token['level'], |
|
| 1066 | self.token['text'], |
|
| 1067 | ) |
|
| 1068 | ||
| 1069 | def output_code(self): |
|
| 1070 | return self.renderer.block_code( |
|
| 1071 | self.token['text'], self.token['lang'] |
|
| 1072 | ) |
|
| 1073 | ||
| 1074 | def output_table(self): |
|
| 1075 | aligns = self.token['align'] |
|
| 1076 | aligns_length = len(aligns) |
|
| 1077 | cell = self.renderer.placeholder() |
|
| 1078 | ||
| 1079 | # header part |
|
| 1080 | header = self.renderer.placeholder() |
|
| 1081 | for i, value in enumerate(self.token['header']): |
|
| 1082 | align = aligns[i] if i < aligns_length else None |
|
| 1083 | flags = {'header': True, 'align': align} |
|
| 1084 | cell += self.renderer.table_cell(self.inline(value), **flags) |
|
| 1085 | ||
| 1086 | header += self.renderer.table_row(cell) |
|
| 1087 | ||
| 1088 | # body part |
|
| 1089 | body = self.renderer.placeholder() |
|
| 1090 | for i, row in enumerate(self.token['cells']): |
|
| 1091 | cell = self.renderer.placeholder() |
|
| 1092 | for j, value in enumerate(row): |
|
| 1093 | align = aligns[j] if j < aligns_length else None |
|
| 1094 | flags = {'header': False, 'align': align} |
|
| 1095 | cell += self.renderer.table_cell(self.inline(value), **flags) |
|
| 1096 | body += self.renderer.table_row(cell) |
|
| 1097 | ||
| 1098 | return self.renderer.table(header, body) |
|
| 1099 | ||
| 1100 | def output_block_quote(self): |
|
| 1101 | body = self.renderer.placeholder() |
|
| 1102 | while self.pop()['type'] != 'block_quote_end': |
|
| 1103 | body += self.tok() |
|
| 1104 | return self.renderer.block_quote(body) |
|
| 1105 | ||
| 1106 | def output_list(self): |
|
| 1107 | ordered = self.token['ordered'] |
|
| 1108 | body = self.renderer.placeholder() |
|
| 1109 | while self.pop()['type'] != 'list_end': |
|
| 1110 | body += self.tok() |
|
| 1111 | return self.renderer.list(body, ordered) |
|
| 1112 | ||
| 1113 | def output_list_item(self): |
|
| 1114 | body = self.renderer.placeholder() |
|
| 1115 | while self.pop()['type'] != 'list_item_end': |
|
| 1116 | if self.token['type'] == 'text': |
|
| 1117 | body += self.tok_text() |
|
| 1118 | else: |
|
| 1119 | body += self.tok() |
|
| 1120 | ||
| 1121 | return self.renderer.list_item(body) |
|
| 1122 | ||
| 1123 | def output_loose_item(self): |
|
| 1124 | body = self.renderer.placeholder() |
|
| 1125 | while self.pop()['type'] != 'list_item_end': |
|
| 1126 | body += self.tok() |
|
| 1127 | return self.renderer.list_item(body) |
|
| 1128 | ||
| 1129 | def output_footnote(self): |
|
| 1130 | self.inline._in_footnote = True |
|
| 1131 | body = self.renderer.placeholder() |
|
| 1132 | key = self.token['key'] |
|
| 1133 | while self.pop()['type'] != 'footnote_end': |
|
| 1134 | body += self.tok() |
|
| 1135 | self.footnotes.append({'key': key, 'text': body}) |
|
| 1136 | self.inline._in_footnote = False |
|
| 1137 | return self.renderer.placeholder() |
|
| 1138 | ||
| 1139 | def output_close_html(self): |
|
| 1140 | text = self.token['text'] |
|
| 1141 | return self.renderer.block_html(text) |
|
| 1142 | ||
| 1143 | def output_open_html(self): |
|
| 1144 | text = self.token['text'] |
|
| 1145 | tag = self.token['tag'] |
|
| 1146 | if self._parse_block_html and tag not in _pre_tags: |
|
| 1147 | text = self.inline(text, rules=self.inline.inline_html_rules) |
|
| 1148 | extra = self.token.get('extra') or '' |
|
| 1149 | html = '<%s%s>%s</%s>' % (tag, extra, text, tag) |
|
| 1150 | return self.renderer.block_html(html) |
|
| 1151 | ||
| 1152 | def output_paragraph(self): |
|
| 1153 | return self.renderer.paragraph(self.inline(self.token['text'])) |
|
| 1154 | ||
| 1155 | def output_text(self): |
|
| 1156 | return self.renderer.paragraph(self.tok_text()) |
|
| 1157 | ||
| 1158 | ||
| 1159 | def markdown(text, escape=True, **kwargs): |
|