Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Side by Side Diff: third_party/Python-Markdown/markdown/treeprocessors.py

Issue 1356203004: Check in a simple pure-python based Markdown previewer. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@add
Patch Set: fix license file Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # markdown is released under the BSD license
2 # Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
3 # Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
4 # Copyright 2004 Manfred Stienstra (the original version)
5 #
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above copyright
14 # notice, this list of conditions and the following disclaimer in the
15 # documentation and/or other materials provided with the distribution.
16 # * Neither the name of the <organization> nor the
17 # names of its contributors may be used to endorse or promote products
18 # derived from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY
21 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 # DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT
24 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31
32
33 from __future__ import unicode_literals 1 from __future__ import unicode_literals
34 from __future__ import absolute_import 2 from __future__ import absolute_import
35 from . import util 3 from . import util
36 from . import odict 4 from . import odict
37 from . import inlinepatterns 5 from . import inlinepatterns
38 6
39 7
40 def build_treeprocessors(md_instance, **kwargs): 8 def build_treeprocessors(md_instance, **kwargs):
41 """ Build the default treeprocessors for Markdown. """ 9 """ Build the default treeprocessors for Markdown. """
42 treeprocessors = odict.OrderedDict() 10 treeprocessors = odict.OrderedDict()
(...skipping 16 matching lines...) Expand all
59 Each Treeprocessor implements a "run" method that takes a pointer to an 27 Each Treeprocessor implements a "run" method that takes a pointer to an
60 ElementTree, modifies it as necessary and returns an ElementTree 28 ElementTree, modifies it as necessary and returns an ElementTree
61 object. 29 object.
62 30
63 Treeprocessors must extend markdown.Treeprocessor. 31 Treeprocessors must extend markdown.Treeprocessor.
64 32
65 """ 33 """
66 def run(self, root): 34 def run(self, root):
67 """ 35 """
68 Subclasses of Treeprocessor should implement a `run` method, which 36 Subclasses of Treeprocessor should implement a `run` method, which
69 takes a root ElementTree. This method can return another ElementTree 37 takes a root ElementTree. This method can return another ElementTree
70 object, and the existing root ElementTree will be replaced, or it can 38 object, and the existing root ElementTree will be replaced, or it can
71 modify the current tree and return None. 39 modify the current tree and return None.
72 """ 40 """
73 pass 41 pass # pragma: no cover
74 42
75 43
76 class InlineProcessor(Treeprocessor): 44 class InlineProcessor(Treeprocessor):
77 """ 45 """
78 A Treeprocessor that traverses a tree, applying inline patterns. 46 A Treeprocessor that traverses a tree, applying inline patterns.
79 """ 47 """
80 48
81 def __init__(self, md): 49 def __init__(self, md):
82 self.__placeholder_prefix = util.INLINE_PLACEHOLDER_PREFIX 50 self.__placeholder_prefix = util.INLINE_PLACEHOLDER_PREFIX
83 self.__placeholder_suffix = util.ETX 51 self.__placeholder_suffix = util.ETX
84 self.__placeholder_length = 4 + len(self.__placeholder_prefix) \ 52 self.__placeholder_length = 4 + len(self.__placeholder_prefix) \
85 + len(self.__placeholder_suffix) 53 + len(self.__placeholder_suffix)
86 self.__placeholder_re = util.INLINE_PLACEHOLDER_RE 54 self.__placeholder_re = util.INLINE_PLACEHOLDER_RE
87 self.markdown = md 55 self.markdown = md
56 self.inlinePatterns = md.inlinePatterns
88 57
89 def __makePlaceholder(self, type): 58 def __makePlaceholder(self, type):
90 """ Generate a placeholder """ 59 """ Generate a placeholder """
91 id = "%04d" % len(self.stashed_nodes) 60 id = "%04d" % len(self.stashed_nodes)
92 hash = util.INLINE_PLACEHOLDER % id 61 hash = util.INLINE_PLACEHOLDER % id
93 return hash, id 62 return hash, id
94 63
95 def __findPlaceholder(self, data, index): 64 def __findPlaceholder(self, data, index):
96 """ 65 """
97 Extract id from data string, start from index 66 Extract id from data string, start from index
98 67
99 Keyword arguments: 68 Keyword arguments:
100 69
101 * data: string 70 * data: string
102 * index: index, from which we start search 71 * index: index, from which we start search
103 72
104 Returns: placeholder id and string index, after the found placeholder. 73 Returns: placeholder id and string index, after the found placeholder.
105 74
106 """ 75 """
107 m = self.__placeholder_re.search(data, index) 76 m = self.__placeholder_re.search(data, index)
108 if m: 77 if m:
109 return m.group(1), m.end() 78 return m.group(1), m.end()
110 else: 79 else:
111 return None, index + 1 80 return None, index + 1
112 81
113 def __stashNode(self, node, type): 82 def __stashNode(self, node, type):
114 """ Add node to stash """ 83 """ Add node to stash """
115 placeholder, id = self.__makePlaceholder(type) 84 placeholder, id = self.__makePlaceholder(type)
116 self.stashed_nodes[id] = node 85 self.stashed_nodes[id] = node
117 return placeholder 86 return placeholder
118 87
119 def __handleInline(self, data, patternIndex=0): 88 def __handleInline(self, data, patternIndex=0):
120 """ 89 """
121 Process string with inline patterns and replace it 90 Process string with inline patterns and replace it
122 with placeholders 91 with placeholders
123 92
124 Keyword arguments: 93 Keyword arguments:
125 94
126 * data: A line of Markdown text 95 * data: A line of Markdown text
127 * patternIndex: The index of the inlinePattern to start with 96 * patternIndex: The index of the inlinePattern to start with
128 97
129 Returns: String with placeholders. 98 Returns: String with placeholders.
130 99
131 """ 100 """
132 if not isinstance(data, util.AtomicString): 101 if not isinstance(data, util.AtomicString):
133 startIndex = 0 102 startIndex = 0
134 while patternIndex < len(self.markdown.inlinePatterns): 103 while patternIndex < len(self.inlinePatterns):
135 data, matched, startIndex = self.__applyPattern( 104 data, matched, startIndex = self.__applyPattern(
136 self.markdown.inlinePatterns.value_for_index(patternIndex), 105 self.inlinePatterns.value_for_index(patternIndex),
137 data, patternIndex, startIndex) 106 data, patternIndex, startIndex)
138 if not matched: 107 if not matched:
139 patternIndex += 1 108 patternIndex += 1
140 return data 109 return data
141 110
142 def __processElementText(self, node, subnode, isText=True): 111 def __processElementText(self, node, subnode, isText=True):
143 """ 112 """
144 Process placeholders in Element.text or Element.tail 113 Process placeholders in Element.text or Element.tail
145 of Elements popped from self.stashed_nodes. 114 of Elements popped from self.stashed_nodes.
146 115
147 Keywords arguments: 116 Keywords arguments:
148 117
149 * node: parent node 118 * node: parent node
150 * subnode: processing node 119 * subnode: processing node
151 * isText: bool variable, True - it's text, False - it's tail 120 * isText: bool variable, True - it's text, False - it's tail
152 121
153 Returns: None 122 Returns: None
154 123
155 """ 124 """
156 if isText: 125 if isText:
157 text = subnode.text 126 text = subnode.text
158 subnode.text = None 127 subnode.text = None
159 else: 128 else:
160 text = subnode.tail 129 text = subnode.tail
161 subnode.tail = None 130 subnode.tail = None
162 131
163 childResult = self.__processPlaceholders(text, subnode) 132 childResult = self.__processPlaceholders(text, subnode, isText)
164 133
165 if not isText and node is not subnode: 134 if not isText and node is not subnode:
166 pos = node.getchildren().index(subnode) 135 pos = list(node).index(subnode) + 1
167 node.remove(subnode)
168 else: 136 else:
169 pos = 0 137 pos = 0
170 138
171 childResult.reverse() 139 childResult.reverse()
172 for newChild in childResult: 140 for newChild in childResult:
173 node.insert(pos, newChild) 141 node.insert(pos, newChild)
174 142
175 def __processPlaceholders(self, data, parent): 143 def __processPlaceholders(self, data, parent, isText=True):
176 """ 144 """
177 Process string with placeholders and generate ElementTree tree. 145 Process string with placeholders and generate ElementTree tree.
178 146
179 Keyword arguments: 147 Keyword arguments:
180 148
181 * data: string with placeholders instead of ElementTree elements. 149 * data: string with placeholders instead of ElementTree elements.
182 * parent: Element, which contains processing inline data 150 * parent: Element, which contains processing inline data
183 151
184 Returns: list with ElementTree elements with applied inline patterns. 152 Returns: list with ElementTree elements with applied inline patterns.
185 153
186 """ 154 """
187 def linkText(text): 155 def linkText(text):
188 if text: 156 if text:
189 if result: 157 if result:
190 if result[-1].tail: 158 if result[-1].tail:
191 result[-1].tail += text 159 result[-1].tail += text
192 else: 160 else:
193 result[-1].tail = text 161 result[-1].tail = text
162 elif not isText:
163 if parent.tail:
164 parent.tail += text
165 else:
166 parent.tail = text
194 else: 167 else:
195 if parent.text: 168 if parent.text:
196 parent.text += text 169 parent.text += text
197 else: 170 else:
198 parent.text = text 171 parent.text = text
199 result = [] 172 result = []
200 strartIndex = 0 173 strartIndex = 0
201 while data: 174 while data:
202 index = data.find(self.__placeholder_prefix, strartIndex) 175 index = data.find(self.__placeholder_prefix, strartIndex)
203 if index != -1: 176 if index != -1:
204 id, phEndIndex = self.__findPlaceholder(data, index) 177 id, phEndIndex = self.__findPlaceholder(data, index)
205 178
206 if id in self.stashed_nodes: 179 if id in self.stashed_nodes:
207 node = self.stashed_nodes.get(id) 180 node = self.stashed_nodes.get(id)
208 181
209 if index > 0: 182 if index > 0:
210 text = data[strartIndex:index] 183 text = data[strartIndex:index]
211 linkText(text) 184 linkText(text)
212 185
213 if not isString(node): # it's Element 186 if not isString(node): # it's Element
214 for child in [node] + node.getchildren(): 187 for child in [node] + list(node):
215 if child.tail: 188 if child.tail:
216 if child.tail.strip(): 189 if child.tail.strip():
217 self.__processElementText(node, child,False) 190 self.__processElementText(
191 node, child, False
192 )
218 if child.text: 193 if child.text:
219 if child.text.strip(): 194 if child.text.strip():
220 self.__processElementText(child, child) 195 self.__processElementText(child, child)
221 else: # it's just a string 196 else: # it's just a string
222 linkText(node) 197 linkText(node)
223 strartIndex = phEndIndex 198 strartIndex = phEndIndex
224 continue 199 continue
225 200
226 strartIndex = phEndIndex 201 strartIndex = phEndIndex
227 result.append(node) 202 result.append(node)
228 203
229 else: # wrong placeholder 204 else: # wrong placeholder
230 end = index + len(self.__placeholder_prefix) 205 end = index + len(self.__placeholder_prefix)
231 linkText(data[strartIndex:end]) 206 linkText(data[strartIndex:end])
232 strartIndex = end 207 strartIndex = end
233 else: 208 else:
234 text = data[strartIndex:] 209 text = data[strartIndex:]
235 if isinstance(data, util.AtomicString): 210 if isinstance(data, util.AtomicString):
236 # We don't want to loose the AtomicString 211 # We don't want to loose the AtomicString
237 text = util.AtomicString(text) 212 text = util.AtomicString(text)
238 linkText(text) 213 linkText(text)
239 data = "" 214 data = ""
(...skipping 22 matching lines...) Expand all
262 return data, False, 0 237 return data, False, 0
263 238
264 node = pattern.handleMatch(match) 239 node = pattern.handleMatch(match)
265 240
266 if node is None: 241 if node is None:
267 return data, True, len(leftData)+match.span(len(match.groups()))[0] 242 return data, True, len(leftData)+match.span(len(match.groups()))[0]
268 243
269 if not isString(node): 244 if not isString(node):
270 if not isinstance(node.text, util.AtomicString): 245 if not isinstance(node.text, util.AtomicString):
271 # We need to process current node too 246 # We need to process current node too
272 for child in [node] + node.getchildren(): 247 for child in [node] + list(node):
273 if not isString(node): 248 if not isString(node):
274 if child.text: 249 if child.text:
275 child.text = self.__handleInline(child.text, 250 child.text = self.__handleInline(
276 patternIndex + 1) 251 child.text, patternIndex + 1
252 )
277 if child.tail: 253 if child.tail:
278 child.tail = self.__handleInline(child.tail, 254 child.tail = self.__handleInline(
279 patternIndex) 255 child.tail, patternIndex
256 )
280 257
281 placeholder = self.__stashNode(node, pattern.type()) 258 placeholder = self.__stashNode(node, pattern.type())
282 259
283 return "%s%s%s%s" % (leftData, 260 return "%s%s%s%s" % (leftData,
284 match.group(1), 261 match.group(1),
285 placeholder, match.groups()[-1]), True, 0 262 placeholder, match.groups()[-1]), True, 0
286 263
287 def run(self, tree): 264 def run(self, tree):
288 """Apply inline patterns to a parsed Markdown tree. 265 """Apply inline patterns to a parsed Markdown tree.
289 266
290 Iterate over ElementTree, find elements with inline tag, apply inline 267 Iterate over ElementTree, find elements with inline tag, apply inline
291 patterns and append newly created Elements to tree. If you don't 268 patterns and append newly created Elements to tree. If you don't
292 want to process your data with inline paterns, instead of normal string, 269 want to process your data with inline paterns, instead of normal
293 use subclass AtomicString: 270 string, use subclass AtomicString:
294 271
295 node.text = markdown.AtomicString("This will not be processed.") 272 node.text = markdown.AtomicString("This will not be processed.")
296 273
297 Arguments: 274 Arguments:
298 275
299 * tree: ElementTree object, representing Markdown tree. 276 * tree: ElementTree object, representing Markdown tree.
300 277
301 Returns: ElementTree object with applied inline patterns. 278 Returns: ElementTree object with applied inline patterns.
302 279
303 """ 280 """
304 self.stashed_nodes = {} 281 self.stashed_nodes = {}
305 282
306 stack = [tree] 283 stack = [tree]
307 284
308 while stack: 285 while stack:
309 currElement = stack.pop() 286 currElement = stack.pop()
310 insertQueue = [] 287 insertQueue = []
311 for child in currElement.getchildren(): 288 for child in currElement:
312 if child.text and not isinstance(child.text, util.AtomicString): 289 if child.text and not isinstance(
290 child.text, util.AtomicString
291 ):
313 text = child.text 292 text = child.text
314 child.text = None 293 child.text = None
315 lst = self.__processPlaceholders(self.__handleInline( 294 lst = self.__processPlaceholders(
316 text), child) 295 self.__handleInline(text), child
296 )
317 stack += lst 297 stack += lst
318 insertQueue.append((child, lst)) 298 insertQueue.append((child, lst))
319 if child.tail: 299 if child.tail:
320 tail = self.__handleInline(child.tail) 300 tail = self.__handleInline(child.tail)
321 dumby = util.etree.Element('d') 301 dumby = util.etree.Element('d')
322 tailResult = self.__processPlaceholders(tail, dumby) 302 child.tail = None
323 if dumby.text: 303 tailResult = self.__processPlaceholders(tail, dumby, False)
324 child.tail = dumby.text 304 if dumby.tail:
325 else: 305 child.tail = dumby.tail
326 child.tail = None 306 pos = list(currElement).index(child) + 1
327 pos = currElement.getchildren().index(child) + 1
328 tailResult.reverse() 307 tailResult.reverse()
329 for newChild in tailResult: 308 for newChild in tailResult:
330 currElement.insert(pos, newChild) 309 currElement.insert(pos, newChild)
331 if child.getchildren(): 310 if len(child):
332 stack.append(child) 311 stack.append(child)
333 312
334 for element, lst in insertQueue: 313 for element, lst in insertQueue:
335 if self.markdown.enable_attributes: 314 if self.markdown.enable_attributes:
336 if element.text and isString(element.text): 315 if element.text and isString(element.text):
337 element.text = \ 316 element.text = inlinepatterns.handleAttributes(
338 inlinepatterns.handleAttributes(element.text, 317 element.text, element
339 element) 318 )
340 i = 0 319 i = 0
341 for newChild in lst: 320 for newChild in lst:
342 if self.markdown.enable_attributes: 321 if self.markdown.enable_attributes:
343 # Processing attributes 322 # Processing attributes
344 if newChild.tail and isString(newChild.tail): 323 if newChild.tail and isString(newChild.tail):
345 newChild.tail = \ 324 newChild.tail = inlinepatterns.handleAttributes(
346 inlinepatterns.handleAttributes(newChild.tail, 325 newChild.tail, element
347 element) 326 )
348 if newChild.text and isString(newChild.text): 327 if newChild.text and isString(newChild.text):
349 newChild.text = \ 328 newChild.text = inlinepatterns.handleAttributes(
350 inlinepatterns.handleAttributes(newChild.text, 329 newChild.text, newChild
351 newChild) 330 )
352 element.insert(i, newChild) 331 element.insert(i, newChild)
353 i += 1 332 i += 1
354 return tree 333 return tree
355 334
356 335
357 class PrettifyTreeprocessor(Treeprocessor): 336 class PrettifyTreeprocessor(Treeprocessor):
358 """ Add linebreaks to the html document. """ 337 """ Add linebreaks to the html document. """
359 338
360 def _prettifyETree(self, elem): 339 def _prettifyETree(self, elem):
361 """ Recursively add linebreaks to ElementTree children. """ 340 """ Recursively add linebreaks to ElementTree children. """
(...skipping 20 matching lines...) Expand all
382 brs = root.getiterator('br') 361 brs = root.getiterator('br')
383 for br in brs: 362 for br in brs:
384 if not br.tail or not br.tail.strip(): 363 if not br.tail or not br.tail.strip():
385 br.tail = '\n' 364 br.tail = '\n'
386 else: 365 else:
387 br.tail = '\n%s' % br.tail 366 br.tail = '\n%s' % br.tail
388 # Clean up extra empty lines at end of code blocks. 367 # Clean up extra empty lines at end of code blocks.
389 pres = root.getiterator('pre') 368 pres = root.getiterator('pre')
390 for pre in pres: 369 for pre in pres:
391 if len(pre) and pre[0].tag == 'code': 370 if len(pre) and pre[0].tag == 'code':
392 pre[0].text = pre[0].text.rstrip() + '\n' 371 pre[0].text = util.AtomicString(pre[0].text.rstrip() + '\n')
OLDNEW
« no previous file with comments | « third_party/Python-Markdown/markdown/serializers.py ('k') | third_party/Python-Markdown/markdown/util.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698