@@ 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): |