##// END OF EJS Templates
markdown: remove smart_strong as it's a default behaviour now
super-admin -
r4923:f4b42406 default
parent child Browse files
Show More
@@ -1,178 +1,176 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import markdown
22 import markdown
23 import xml.etree.ElementTree as etree
23 import xml.etree.ElementTree as etree
24
24
25 from markdown.extensions import Extension
25 from markdown.extensions import Extension
26 from markdown.extensions.fenced_code import FencedCodeExtension
26 from markdown.extensions.fenced_code import FencedCodeExtension
27 from markdown.extensions.smart_strong import SmartEmphasisExtension
28 from markdown.extensions.tables import TableExtension
27 from markdown.extensions.tables import TableExtension
29 from markdown.inlinepatterns import Pattern
28 from markdown.inlinepatterns import Pattern
30
29
31 import gfm
30 import gfm
32
31
33
32
34 class InlineProcessor(Pattern):
33 class InlineProcessor(Pattern):
35 """
34 """
36 Base class that inline patterns subclass.
35 Base class that inline patterns subclass.
37 This is the newer style inline processor that uses a more
36 This is the newer style inline processor that uses a more
38 efficient and flexible search approach.
37 efficient and flexible search approach.
39 """
38 """
40
39
41 def __init__(self, pattern, md=None):
40 def __init__(self, pattern, md=None):
42 """
41 """
43 Create an instant of an inline pattern.
42 Create an instant of an inline pattern.
44 Keyword arguments:
43 Keyword arguments:
45 * pattern: A regular expression that matches a pattern
44 * pattern: A regular expression that matches a pattern
46 """
45 """
47 self.pattern = pattern
46 self.pattern = pattern
48 self.compiled_re = re.compile(pattern, re.DOTALL | re.UNICODE)
47 self.compiled_re = re.compile(pattern, re.DOTALL | re.UNICODE)
49
48
50 # Api for Markdown to pass safe_mode into instance
49 # Api for Markdown to pass safe_mode into instance
51 self.safe_mode = False
50 self.safe_mode = False
52 self.md = md
51 self.md = md
53
52
54 def handleMatch(self, m, data):
53 def handleMatch(self, m, data):
55 """Return a ElementTree element from the given match and the
54 """Return a ElementTree element from the given match and the
56 start and end index of the matched text.
55 start and end index of the matched text.
57 If `start` and/or `end` are returned as `None`, it will be
56 If `start` and/or `end` are returned as `None`, it will be
58 assumed that the processor did not find a valid region of text.
57 assumed that the processor did not find a valid region of text.
59 Subclasses should override this method.
58 Subclasses should override this method.
60 Keyword arguments:
59 Keyword arguments:
61 * m: A re match object containing a match of the pattern.
60 * m: A re match object containing a match of the pattern.
62 * data: The buffer current under analysis
61 * data: The buffer current under analysis
63 Returns:
62 Returns:
64 * el: The ElementTree element, text or None.
63 * el: The ElementTree element, text or None.
65 * start: The start of the region that has been matched or None.
64 * start: The start of the region that has been matched or None.
66 * end: The end of the region that has been matched or None.
65 * end: The end of the region that has been matched or None.
67 """
66 """
68 pass # pragma: no cover
67 pass # pragma: no cover
69
68
70
69
71 class SimpleTagInlineProcessor(InlineProcessor):
70 class SimpleTagInlineProcessor(InlineProcessor):
72 """
71 """
73 Return element of type `tag` with a text attribute of group(2)
72 Return element of type `tag` with a text attribute of group(2)
74 of a Pattern.
73 of a Pattern.
75 """
74 """
76 def __init__(self, pattern, tag):
75 def __init__(self, pattern, tag):
77 InlineProcessor.__init__(self, pattern)
76 InlineProcessor.__init__(self, pattern)
78 self.tag = tag
77 self.tag = tag
79
78
80 def handleMatch(self, m, data): # pragma: no cover
79 def handleMatch(self, m, data): # pragma: no cover
81 el = etree.Element(self.tag)
80 el = etree.Element(self.tag)
82 el.text = m.group(2)
81 el.text = m.group(2)
83 return el, m.start(0), m.end(0)
82 return el, m.start(0), m.end(0)
84
83
85
84
86 class SubstituteTagInlineProcessor(SimpleTagInlineProcessor):
85 class SubstituteTagInlineProcessor(SimpleTagInlineProcessor):
87 """ Return an element of type `tag` with no children. """
86 """ Return an element of type `tag` with no children. """
88 def handleMatch(self, m, data):
87 def handleMatch(self, m, data):
89 return etree.Element(self.tag), m.start(0), m.end(0)
88 return etree.Element(self.tag), m.start(0), m.end(0)
90
89
91
90
92 class Nl2BrExtension(Extension):
91 class Nl2BrExtension(Extension):
93 BR_RE = r'\n'
92 BR_RE = r'\n'
94
93
95 def extendMarkdown(self, md, md_globals):
94 def extendMarkdown(self, md, md_globals):
96 br_tag = SubstituteTagInlineProcessor(self.BR_RE, 'br')
95 br_tag = SubstituteTagInlineProcessor(self.BR_RE, 'br')
97 md.inlinePatterns.add('nl', br_tag, '_end')
96 md.inlinePatterns.add('nl', br_tag, '_end')
98
97
99
98
100 class GithubFlavoredMarkdownExtension(Extension):
99 class GithubFlavoredMarkdownExtension(Extension):
101 """
100 """
102 An extension that is as compatible as possible with GitHub-flavored
101 An extension that is as compatible as possible with GitHub-flavored
103 Markdown (GFM).
102 Markdown (GFM).
104
103
105 This extension aims to be compatible with the variant of GFM that GitHub
104 This extension aims to be compatible with the variant of GFM that GitHub
106 uses for Markdown-formatted gists and files (including READMEs). This
105 uses for Markdown-formatted gists and files (including READMEs). This
107 variant seems to have all the extensions described in the `GFM
106 variant seems to have all the extensions described in the `GFM
108 documentation`_, except:
107 documentation`_, except:
109
108
110 - Newlines in paragraphs are not transformed into ``br`` tags.
109 - Newlines in paragraphs are not transformed into ``br`` tags.
111 - Intra-GitHub links to commits, repositories, and issues are not
110 - Intra-GitHub links to commits, repositories, and issues are not
112 supported.
111 supported.
113
112
114 If you need support for features specific to GitHub comments and issues,
113 If you need support for features specific to GitHub comments and issues,
115 please use :class:`mdx_gfm.GithubFlavoredMarkdownExtension`.
114 please use :class:`mdx_gfm.GithubFlavoredMarkdownExtension`.
116
115
117 .. _GFM documentation: https://guides.github.com/features/mastering-markdown/
116 .. _GFM documentation: https://guides.github.com/features/mastering-markdown/
118 """
117 """
119
118
120 def extendMarkdown(self, md, md_globals):
119 def extendMarkdown(self, md, md_globals):
121 # Built-in extensions
120 # Built-in extensions
122 Nl2BrExtension().extendMarkdown(md, md_globals)
121 Nl2BrExtension().extendMarkdown(md, md_globals)
123 FencedCodeExtension().extendMarkdown(md, md_globals)
122 FencedCodeExtension().extendMarkdown(md, md_globals)
124 SmartEmphasisExtension().extendMarkdown(md, md_globals)
125 TableExtension().extendMarkdown(md, md_globals)
123 TableExtension().extendMarkdown(md, md_globals)
126
124
127 # Custom extensions
125 # Custom extensions
128 gfm.AutolinkExtension().extendMarkdown(md, md_globals)
126 gfm.AutolinkExtension().extendMarkdown(md, md_globals)
129 gfm.AutomailExtension().extendMarkdown(md, md_globals)
127 gfm.AutomailExtension().extendMarkdown(md, md_globals)
130 gfm.HiddenHiliteExtension([
128 gfm.HiddenHiliteExtension([
131 ('guess_lang', 'False'),
129 ('guess_lang', 'False'),
132 ('css_class', 'highlight')
130 ('css_class', 'highlight')
133 ]).extendMarkdown(md, md_globals)
131 ]).extendMarkdown(md, md_globals)
134 gfm.SemiSaneListExtension().extendMarkdown(md, md_globals)
132 gfm.SemiSaneListExtension().extendMarkdown(md, md_globals)
135 gfm.SpacedLinkExtension().extendMarkdown(md, md_globals)
133 gfm.SpacedLinkExtension().extendMarkdown(md, md_globals)
136 gfm.StrikethroughExtension().extendMarkdown(md, md_globals)
134 gfm.StrikethroughExtension().extendMarkdown(md, md_globals)
137 gfm.TaskListExtension([
135 gfm.TaskListExtension([
138 ('list_attrs', {'class': 'checkbox'})
136 ('list_attrs', {'class': 'checkbox'})
139 ]).extendMarkdown(md, md_globals)
137 ]).extendMarkdown(md, md_globals)
140
138
141
139
142 # Global Vars
140 # Global Vars
143 URLIZE_RE = '(%s)' % '|'.join([
141 URLIZE_RE = '(%s)' % '|'.join([
144 r'<(?:f|ht)tps?://[^>]*>',
142 r'<(?:f|ht)tps?://[^>]*>',
145 r'\b(?:f|ht)tps?://[^)<>\s]+[^.,)<>\s]',
143 r'\b(?:f|ht)tps?://[^)<>\s]+[^.,)<>\s]',
146 r'\bwww\.[^)<>\s]+[^.,)<>\s]',
144 r'\bwww\.[^)<>\s]+[^.,)<>\s]',
147 r'[^(<\s]+\.(?:com|net|org)\b',
145 r'[^(<\s]+\.(?:com|net|org)\b',
148 ])
146 ])
149
147
150
148
151 class UrlizePattern(markdown.inlinepatterns.Pattern):
149 class UrlizePattern(markdown.inlinepatterns.Pattern):
152 """ Return a link Element given an autolink (`http://example/com`). """
150 """ Return a link Element given an autolink (`http://example/com`). """
153 def handleMatch(self, m):
151 def handleMatch(self, m):
154 url = m.group(2)
152 url = m.group(2)
155
153
156 if url.startswith('<'):
154 if url.startswith('<'):
157 url = url[1:-1]
155 url = url[1:-1]
158
156
159 text = url
157 text = url
160
158
161 if not url.split('://')[0] in ('http','https','ftp'):
159 if not url.split('://')[0] in ('http','https','ftp'):
162 if '@' in url and not '/' in url:
160 if '@' in url and not '/' in url:
163 url = 'mailto:' + url
161 url = 'mailto:' + url
164 else:
162 else:
165 url = 'http://' + url
163 url = 'http://' + url
166
164
167 el = markdown.util.etree.Element("a")
165 el = markdown.util.etree.Element("a")
168 el.set('href', url)
166 el.set('href', url)
169 el.text = markdown.util.AtomicString(text)
167 el.text = markdown.util.AtomicString(text)
170 return el
168 return el
171
169
172
170
173 class UrlizeExtension(markdown.Extension):
171 class UrlizeExtension(markdown.Extension):
174 """ Urlize Extension for Python-Markdown. """
172 """ Urlize Extension for Python-Markdown. """
175
173
176 def extendMarkdown(self, md, md_globals):
174 def extendMarkdown(self, md, md_globals):
177 """ Replace autolink with UrlizePattern """
175 """ Replace autolink with UrlizePattern """
178 md.inlinePatterns['autolink'] = UrlizePattern(URLIZE_RE, md)
176 md.inlinePatterns['autolink'] = UrlizePattern(URLIZE_RE, md)
General Comments 0
You need to be logged in to leave comments. Login now