inlinepatterns.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. """
  2. Python Markdown
  3. A Python implementation of John Gruber's Markdown.
  4. Documentation: https://python-markdown.github.io/
  5. GitHub: https://github.com/Python-Markdown/markdown/
  6. PyPI: https://pypi.org/project/Markdown/
  7. Started by Manfred Stienstra (http://www.dwerg.net/).
  8. Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
  9. Currently maintained by Waylan Limberg (https://github.com/waylan),
  10. Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
  11. Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
  12. Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
  13. Copyright 2004 Manfred Stienstra (the original version)
  14. License: BSD (see LICENSE.md for details).
  15. INLINE PATTERNS
  16. =============================================================================
  17. Inline patterns such as *emphasis* are handled by means of auxiliary
  18. objects, one per pattern. Pattern objects must be instances of classes
  19. that extend markdown.Pattern. Each pattern object uses a single regular
  20. expression and needs support the following methods:
  21. pattern.getCompiledRegExp() # returns a regular expression
  22. pattern.handleMatch(m) # takes a match object and returns
  23. # an ElementTree element or just plain text
  24. All of python markdown's built-in patterns subclass from Pattern,
  25. but you can add additional patterns that don't.
  26. Also note that all the regular expressions used by inline must
  27. capture the whole block. For this reason, they all start with
  28. '^(.*)' and end with '(.*)!'. In case with built-in expression
  29. Pattern takes care of adding the "^(.*)" and "(.*)!".
  30. Finally, the order in which regular expressions are applied is very
  31. important - e.g. if we first replace http://.../ links with <a> tags
  32. and _then_ try to replace inline html, we would end up with a mess.
  33. So, we apply the expressions in the following order:
  34. * escape and backticks have to go before everything else, so
  35. that we can preempt any markdown patterns by escaping them.
  36. * then we handle auto-links (must be done before inline html)
  37. * then we handle inline HTML. At this point we will simply
  38. replace all inline HTML strings with a placeholder and add
  39. the actual HTML to a hash.
  40. * then inline images (must be done before links)
  41. * then bracketed links, first regular then reference-style
  42. * finally we apply strong and emphasis
  43. """
  44. from . import util
  45. from collections import namedtuple
  46. import re
  47. import xml.etree.ElementTree as etree
  48. try: # pragma: no cover
  49. from html import entities
  50. except ImportError: # pragma: no cover
  51. import htmlentitydefs as entities
  52. def build_inlinepatterns(md, **kwargs):
  53. """ Build the default set of inline patterns for Markdown. """
  54. inlinePatterns = util.Registry()
  55. inlinePatterns.register(BacktickInlineProcessor(BACKTICK_RE), 'backtick', 190)
  56. inlinePatterns.register(EscapeInlineProcessor(ESCAPE_RE, md), 'escape', 180)
  57. inlinePatterns.register(ReferenceInlineProcessor(REFERENCE_RE, md), 'reference', 170)
  58. inlinePatterns.register(LinkInlineProcessor(LINK_RE, md), 'link', 160)
  59. inlinePatterns.register(ImageInlineProcessor(IMAGE_LINK_RE, md), 'image_link', 150)
  60. inlinePatterns.register(
  61. ImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md), 'image_reference', 140
  62. )
  63. inlinePatterns.register(
  64. ShortReferenceInlineProcessor(REFERENCE_RE, md), 'short_reference', 130
  65. )
  66. inlinePatterns.register(AutolinkInlineProcessor(AUTOLINK_RE, md), 'autolink', 120)
  67. inlinePatterns.register(AutomailInlineProcessor(AUTOMAIL_RE, md), 'automail', 110)
  68. inlinePatterns.register(SubstituteTagInlineProcessor(LINE_BREAK_RE, 'br'), 'linebreak', 100)
  69. inlinePatterns.register(HtmlInlineProcessor(HTML_RE, md), 'html', 90)
  70. inlinePatterns.register(HtmlInlineProcessor(ENTITY_RE, md), 'entity', 80)
  71. inlinePatterns.register(SimpleTextInlineProcessor(NOT_STRONG_RE), 'not_strong', 70)
  72. inlinePatterns.register(AsteriskProcessor(r'\*'), 'em_strong', 60)
  73. inlinePatterns.register(UnderscoreProcessor(r'_'), 'em_strong2', 50)
  74. return inlinePatterns
  75. """
  76. The actual regular expressions for patterns
  77. -----------------------------------------------------------------------------
  78. """
  79. NOIMG = r'(?<!\!)'
  80. # `e=f()` or ``e=f("`")``
  81. BACKTICK_RE = r'(?:(?<!\\)((?:\\{2})+)(?=`+)|(?<!\\)(`+)(.+?)(?<!`)\2(?!`))'
  82. # \<
  83. ESCAPE_RE = r'\\(.)'
  84. # *emphasis*
  85. EMPHASIS_RE = r'(\*)([^\*]+)\1'
  86. # **strong**
  87. STRONG_RE = r'(\*{2})(.+?)\1'
  88. # __smart__strong__
  89. SMART_STRONG_RE = r'(?<!\w)(_{2})(?!_)(.+?)(?<!_)\1(?!\w)'
  90. # _smart_emphasis_
  91. SMART_EMPHASIS_RE = r'(?<!\w)(_)(?!_)(.+?)(?<!_)\1(?!\w)'
  92. # __strong _em__
  93. SMART_STRONG_EM_RE = r'(?<!\w)(\_)\1(?!\1)(.+?)(?<!\w)\1(?!\1)(.+?)\1{3}(?!\w)'
  94. # ***strongem*** or ***em*strong**
  95. EM_STRONG_RE = r'(\*)\1{2}(.+?)\1(.*?)\1{2}'
  96. # ___strongem___ or ___em_strong__
  97. EM_STRONG2_RE = r'(_)\1{2}(.+?)\1(.*?)\1{2}'
  98. # ***strong**em*
  99. STRONG_EM_RE = r'(\*)\1{2}(.+?)\1{2}(.*?)\1'
  100. # ___strong__em_
  101. STRONG_EM2_RE = r'(_)\1{2}(.+?)\1{2}(.*?)\1'
  102. # __strong_em___
  103. STRONG_EM3_RE = r'(\*)\1(?!\1)(.+?)\1(?!\1)(.+?)\1{3}'
  104. # [text](url) or [text](<url>) or [text](url "title")
  105. LINK_RE = NOIMG + r'\['
  106. # ![alttxt](http://x.com/) or ![alttxt](<http://x.com/>)
  107. IMAGE_LINK_RE = r'\!\['
  108. # [Google][3]
  109. REFERENCE_RE = LINK_RE
  110. # ![alt text][2]
  111. IMAGE_REFERENCE_RE = IMAGE_LINK_RE
  112. # stand-alone * or _
  113. NOT_STRONG_RE = r'((^|\s)(\*|_)(\s|$))'
  114. # <http://www.123.com>
  115. AUTOLINK_RE = r'<((?:[Ff]|[Hh][Tt])[Tt][Pp][Ss]?://[^<>]*)>'
  116. # <me@example.com>
  117. AUTOMAIL_RE = r'<([^<> !]*@[^@<> ]*)>'
  118. # <...>
  119. HTML_RE = r'(<([a-zA-Z/][^<>]*|!--(?:(?!<!--|-->).)*--)>)'
  120. # "&#38;" (decimal) or "&#x26;" (hex) or "&amp;" (named)
  121. ENTITY_RE = r'(&(?:\#[0-9]+|\#x[0-9a-fA-F]+|[a-zA-Z0-9]+);)'
  122. # two spaces at end of line
  123. LINE_BREAK_RE = r' \n'
  124. def dequote(string):
  125. """Remove quotes from around a string."""
  126. if ((string.startswith('"') and string.endswith('"')) or
  127. (string.startswith("'") and string.endswith("'"))):
  128. return string[1:-1]
  129. else:
  130. return string
  131. class EmStrongItem(namedtuple('EmStrongItem', ['pattern', 'builder', 'tags'])):
  132. """Emphasis/strong pattern item."""
  133. """
  134. The pattern classes
  135. -----------------------------------------------------------------------------
  136. """
  137. class Pattern: # pragma: no cover
  138. """Base class that inline patterns subclass. """
  139. ANCESTOR_EXCLUDES = tuple()
  140. def __init__(self, pattern, md=None):
  141. """
  142. Create an instant of an inline pattern.
  143. Keyword arguments:
  144. * pattern: A regular expression that matches a pattern
  145. """
  146. self.pattern = pattern
  147. self.compiled_re = re.compile(r"^(.*?)%s(.*)$" % pattern,
  148. re.DOTALL | re.UNICODE)
  149. self.md = md
  150. @property
  151. @util.deprecated("Use 'md' instead.")
  152. def markdown(self):
  153. # TODO: remove this later
  154. return self.md
  155. def getCompiledRegExp(self):
  156. """ Return a compiled regular expression. """
  157. return self.compiled_re
  158. def handleMatch(self, m):
  159. """Return a ElementTree element from the given match.
  160. Subclasses should override this method.
  161. Keyword arguments:
  162. * m: A re match object containing a match of the pattern.
  163. """
  164. pass # pragma: no cover
  165. def type(self):
  166. """ Return class name, to define pattern type """
  167. return self.__class__.__name__
  168. def unescape(self, text):
  169. """ Return unescaped text given text with an inline placeholder. """
  170. try:
  171. stash = self.md.treeprocessors['inline'].stashed_nodes
  172. except KeyError: # pragma: no cover
  173. return text
  174. def get_stash(m):
  175. id = m.group(1)
  176. if id in stash:
  177. value = stash.get(id)
  178. if isinstance(value, str):
  179. return value
  180. else:
  181. # An etree Element - return text content only
  182. return ''.join(value.itertext())
  183. return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
  184. class InlineProcessor(Pattern):
  185. """
  186. Base class that inline patterns subclass.
  187. This is the newer style inline processor that uses a more
  188. efficient and flexible search approach.
  189. """
  190. def __init__(self, pattern, md=None):
  191. """
  192. Create an instant of an inline pattern.
  193. Keyword arguments:
  194. * pattern: A regular expression that matches a pattern
  195. """
  196. self.pattern = pattern
  197. self.compiled_re = re.compile(pattern, re.DOTALL | re.UNICODE)
  198. # Api for Markdown to pass safe_mode into instance
  199. self.safe_mode = False
  200. self.md = md
  201. def handleMatch(self, m, data):
  202. """Return a ElementTree element from the given match and the
  203. start and end index of the matched text.
  204. If `start` and/or `end` are returned as `None`, it will be
  205. assumed that the processor did not find a valid region of text.
  206. Subclasses should override this method.
  207. Keyword arguments:
  208. * m: A re match object containing a match of the pattern.
  209. * data: The buffer current under analysis
  210. Returns:
  211. * el: The ElementTree element, text or None.
  212. * start: The start of the region that has been matched or None.
  213. * end: The end of the region that has been matched or None.
  214. """
  215. pass # pragma: no cover
  216. class SimpleTextPattern(Pattern): # pragma: no cover
  217. """ Return a simple text of group(2) of a Pattern. """
  218. def handleMatch(self, m):
  219. return m.group(2)
  220. class SimpleTextInlineProcessor(InlineProcessor):
  221. """ Return a simple text of group(1) of a Pattern. """
  222. def handleMatch(self, m, data):
  223. return m.group(1), m.start(0), m.end(0)
  224. class EscapeInlineProcessor(InlineProcessor):
  225. """ Return an escaped character. """
  226. def handleMatch(self, m, data):
  227. char = m.group(1)
  228. if char in self.md.ESCAPED_CHARS:
  229. return '{}{}{}'.format(util.STX, ord(char), util.ETX), m.start(0), m.end(0)
  230. else:
  231. return None, m.start(0), m.end(0)
  232. class SimpleTagPattern(Pattern): # pragma: no cover
  233. """
  234. Return element of type `tag` with a text attribute of group(3)
  235. of a Pattern.
  236. """
  237. def __init__(self, pattern, tag):
  238. Pattern.__init__(self, pattern)
  239. self.tag = tag
  240. def handleMatch(self, m):
  241. el = etree.Element(self.tag)
  242. el.text = m.group(3)
  243. return el
  244. class SimpleTagInlineProcessor(InlineProcessor):
  245. """
  246. Return element of type `tag` with a text attribute of group(2)
  247. of a Pattern.
  248. """
  249. def __init__(self, pattern, tag):
  250. InlineProcessor.__init__(self, pattern)
  251. self.tag = tag
  252. def handleMatch(self, m, data): # pragma: no cover
  253. el = etree.Element(self.tag)
  254. el.text = m.group(2)
  255. return el, m.start(0), m.end(0)
  256. class SubstituteTagPattern(SimpleTagPattern): # pragma: no cover
  257. """ Return an element of type `tag` with no children. """
  258. def handleMatch(self, m):
  259. return etree.Element(self.tag)
  260. class SubstituteTagInlineProcessor(SimpleTagInlineProcessor):
  261. """ Return an element of type `tag` with no children. """
  262. def handleMatch(self, m, data):
  263. return etree.Element(self.tag), m.start(0), m.end(0)
  264. class BacktickInlineProcessor(InlineProcessor):
  265. """ Return a `<code>` element containing the matching text. """
  266. def __init__(self, pattern):
  267. InlineProcessor.__init__(self, pattern)
  268. self.ESCAPED_BSLASH = '{}{}{}'.format(util.STX, ord('\\'), util.ETX)
  269. self.tag = 'code'
  270. def handleMatch(self, m, data):
  271. if m.group(3):
  272. el = etree.Element(self.tag)
  273. el.text = util.AtomicString(util.code_escape(m.group(3).strip()))
  274. return el, m.start(0), m.end(0)
  275. else:
  276. return m.group(1).replace('\\\\', self.ESCAPED_BSLASH), m.start(0), m.end(0)
  277. class DoubleTagPattern(SimpleTagPattern): # pragma: no cover
  278. """Return a ElementTree element nested in tag2 nested in tag1.
  279. Useful for strong emphasis etc.
  280. """
  281. def handleMatch(self, m):
  282. tag1, tag2 = self.tag.split(",")
  283. el1 = etree.Element(tag1)
  284. el2 = etree.SubElement(el1, tag2)
  285. el2.text = m.group(3)
  286. if len(m.groups()) == 5:
  287. el2.tail = m.group(4)
  288. return el1
  289. class DoubleTagInlineProcessor(SimpleTagInlineProcessor):
  290. """Return a ElementTree element nested in tag2 nested in tag1.
  291. Useful for strong emphasis etc.
  292. """
  293. def handleMatch(self, m, data): # pragma: no cover
  294. tag1, tag2 = self.tag.split(",")
  295. el1 = etree.Element(tag1)
  296. el2 = etree.SubElement(el1, tag2)
  297. el2.text = m.group(2)
  298. if len(m.groups()) == 3:
  299. el2.tail = m.group(3)
  300. return el1, m.start(0), m.end(0)
  301. class HtmlInlineProcessor(InlineProcessor):
  302. """ Store raw inline html and return a placeholder. """
  303. def handleMatch(self, m, data):
  304. rawhtml = self.unescape(m.group(1))
  305. place_holder = self.md.htmlStash.store(rawhtml)
  306. return place_holder, m.start(0), m.end(0)
  307. def unescape(self, text):
  308. """ Return unescaped text given text with an inline placeholder. """
  309. try:
  310. stash = self.md.treeprocessors['inline'].stashed_nodes
  311. except KeyError: # pragma: no cover
  312. return text
  313. def get_stash(m):
  314. id = m.group(1)
  315. value = stash.get(id)
  316. if value is not None:
  317. try:
  318. return self.md.serializer(value)
  319. except Exception:
  320. return r'\%s' % value
  321. return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
  322. class AsteriskProcessor(InlineProcessor):
  323. """Emphasis processor for handling strong and em matches inside asterisks."""
  324. PATTERNS = [
  325. EmStrongItem(re.compile(EM_STRONG_RE, re.DOTALL | re.UNICODE), 'double', 'strong,em'),
  326. EmStrongItem(re.compile(STRONG_EM_RE, re.DOTALL | re.UNICODE), 'double', 'em,strong'),
  327. EmStrongItem(re.compile(STRONG_EM3_RE, re.DOTALL | re.UNICODE), 'double2', 'strong,em'),
  328. EmStrongItem(re.compile(STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'),
  329. EmStrongItem(re.compile(EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em')
  330. ]
  331. def build_single(self, m, tag, idx):
  332. """Return single tag."""
  333. el1 = etree.Element(tag)
  334. text = m.group(2)
  335. self.parse_sub_patterns(text, el1, None, idx)
  336. return el1
  337. def build_double(self, m, tags, idx):
  338. """Return double tag."""
  339. tag1, tag2 = tags.split(",")
  340. el1 = etree.Element(tag1)
  341. el2 = etree.Element(tag2)
  342. text = m.group(2)
  343. self.parse_sub_patterns(text, el2, None, idx)
  344. el1.append(el2)
  345. if len(m.groups()) == 3:
  346. text = m.group(3)
  347. self.parse_sub_patterns(text, el1, el2, idx)
  348. return el1
  349. def build_double2(self, m, tags, idx):
  350. """Return double tags (variant 2): `<strong>text <em>text</em></strong>`."""
  351. tag1, tag2 = tags.split(",")
  352. el1 = etree.Element(tag1)
  353. el2 = etree.Element(tag2)
  354. text = m.group(2)
  355. self.parse_sub_patterns(text, el1, None, idx)
  356. text = m.group(3)
  357. el1.append(el2)
  358. self.parse_sub_patterns(text, el2, None, idx)
  359. return el1
  360. def parse_sub_patterns(self, data, parent, last, idx):
  361. """
  362. Parses sub patterns.
  363. `data` (`str`):
  364. text to evaluate.
  365. `parent` (`etree.Element`):
  366. Parent to attach text and sub elements to.
  367. `last` (`etree.Element`):
  368. Last appended child to parent. Can also be None if parent has no children.
  369. `idx` (`int`):
  370. Current pattern index that was used to evaluate the parent.
  371. """
  372. offset = 0
  373. pos = 0
  374. length = len(data)
  375. while pos < length:
  376. # Find the start of potential emphasis or strong tokens
  377. if self.compiled_re.match(data, pos):
  378. matched = False
  379. # See if the we can match an emphasis/strong pattern
  380. for index, item in enumerate(self.PATTERNS):
  381. # Only evaluate patterns that are after what was used on the parent
  382. if index <= idx:
  383. continue
  384. m = item.pattern.match(data, pos)
  385. if m:
  386. # Append child nodes to parent
  387. # Text nodes should be appended to the last
  388. # child if present, and if not, it should
  389. # be added as the parent's text node.
  390. text = data[offset:m.start(0)]
  391. if text:
  392. if last is not None:
  393. last.tail = text
  394. else:
  395. parent.text = text
  396. el = self.build_element(m, item.builder, item.tags, index)
  397. parent.append(el)
  398. last = el
  399. # Move our position past the matched hunk
  400. offset = pos = m.end(0)
  401. matched = True
  402. if not matched:
  403. # We matched nothing, move on to the next character
  404. pos += 1
  405. else:
  406. # Increment position as no potential emphasis start was found.
  407. pos += 1
  408. # Append any leftover text as a text node.
  409. text = data[offset:]
  410. if text:
  411. if last is not None:
  412. last.tail = text
  413. else:
  414. parent.text = text
  415. def build_element(self, m, builder, tags, index):
  416. """Element builder."""
  417. if builder == 'double2':
  418. return self.build_double2(m, tags, index)
  419. elif builder == 'double':
  420. return self.build_double(m, tags, index)
  421. else:
  422. return self.build_single(m, tags, index)
  423. def handleMatch(self, m, data):
  424. """Parse patterns."""
  425. el = None
  426. start = None
  427. end = None
  428. for index, item in enumerate(self.PATTERNS):
  429. m1 = item.pattern.match(data, m.start(0))
  430. if m1:
  431. start = m1.start(0)
  432. end = m1.end(0)
  433. el = self.build_element(m1, item.builder, item.tags, index)
  434. break
  435. return el, start, end
  436. class UnderscoreProcessor(AsteriskProcessor):
  437. """Emphasis processor for handling strong and em matches inside underscores."""
  438. PATTERNS = [
  439. EmStrongItem(re.compile(EM_STRONG2_RE, re.DOTALL | re.UNICODE), 'double', 'strong,em'),
  440. EmStrongItem(re.compile(STRONG_EM2_RE, re.DOTALL | re.UNICODE), 'double', 'em,strong'),
  441. EmStrongItem(re.compile(SMART_STRONG_EM_RE, re.DOTALL | re.UNICODE), 'double2', 'strong,em'),
  442. EmStrongItem(re.compile(SMART_STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'),
  443. EmStrongItem(re.compile(SMART_EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em')
  444. ]
  445. class LinkInlineProcessor(InlineProcessor):
  446. """ Return a link element from the given match. """
  447. RE_LINK = re.compile(r'''\(\s*(?:(<[^<>]*>)\s*(?:('[^']*'|"[^"]*")\s*)?\))?''', re.DOTALL | re.UNICODE)
  448. RE_TITLE_CLEAN = re.compile(r'\s')
  449. def handleMatch(self, m, data):
  450. text, index, handled = self.getText(data, m.end(0))
  451. if not handled:
  452. return None, None, None
  453. href, title, index, handled = self.getLink(data, index)
  454. if not handled:
  455. return None, None, None
  456. el = etree.Element("a")
  457. el.text = text
  458. el.set("href", href)
  459. if title is not None:
  460. el.set("title", title)
  461. return el, m.start(0), index
  462. def getLink(self, data, index):
  463. """Parse data between `()` of `[Text]()` allowing recursive `()`. """
  464. href = ''
  465. title = None
  466. handled = False
  467. m = self.RE_LINK.match(data, pos=index)
  468. if m and m.group(1):
  469. # Matches [Text](<link> "title")
  470. href = m.group(1)[1:-1].strip()
  471. if m.group(2):
  472. title = m.group(2)[1:-1]
  473. index = m.end(0)
  474. handled = True
  475. elif m:
  476. # Track bracket nesting and index in string
  477. bracket_count = 1
  478. backtrack_count = 1
  479. start_index = m.end()
  480. index = start_index
  481. last_bracket = -1
  482. # Primary (first found) quote tracking.
  483. quote = None
  484. start_quote = -1
  485. exit_quote = -1
  486. ignore_matches = False
  487. # Secondary (second found) quote tracking.
  488. alt_quote = None
  489. start_alt_quote = -1
  490. exit_alt_quote = -1
  491. # Track last character
  492. last = ''
  493. for pos in range(index, len(data)):
  494. c = data[pos]
  495. if c == '(':
  496. # Count nested (
  497. # Don't increment the bracket count if we are sure we're in a title.
  498. if not ignore_matches:
  499. bracket_count += 1
  500. elif backtrack_count > 0:
  501. backtrack_count -= 1
  502. elif c == ')':
  503. # Match nested ) to (
  504. # Don't decrement if we are sure we are in a title that is unclosed.
  505. if ((exit_quote != -1 and quote == last) or (exit_alt_quote != -1 and alt_quote == last)):
  506. bracket_count = 0
  507. elif not ignore_matches:
  508. bracket_count -= 1
  509. elif backtrack_count > 0:
  510. backtrack_count -= 1
  511. # We've found our backup end location if the title doesn't reslove.
  512. if backtrack_count == 0:
  513. last_bracket = index + 1
  514. elif c in ("'", '"'):
  515. # Quote has started
  516. if not quote:
  517. # We'll assume we are now in a title.
  518. # Brackets are quoted, so no need to match them (except for the final one).
  519. ignore_matches = True
  520. backtrack_count = bracket_count
  521. bracket_count = 1
  522. start_quote = index + 1
  523. quote = c
  524. # Secondary quote (in case the first doesn't resolve): [text](link'"title")
  525. elif c != quote and not alt_quote:
  526. start_alt_quote = index + 1
  527. alt_quote = c
  528. # Update primary quote match
  529. elif c == quote:
  530. exit_quote = index + 1
  531. # Update secondary quote match
  532. elif alt_quote and c == alt_quote:
  533. exit_alt_quote = index + 1
  534. index += 1
  535. # Link is closed, so let's break out of the loop
  536. if bracket_count == 0:
  537. # Get the title if we closed a title string right before link closed
  538. if exit_quote >= 0 and quote == last:
  539. href = data[start_index:start_quote - 1]
  540. title = ''.join(data[start_quote:exit_quote - 1])
  541. elif exit_alt_quote >= 0 and alt_quote == last:
  542. href = data[start_index:start_alt_quote - 1]
  543. title = ''.join(data[start_alt_quote:exit_alt_quote - 1])
  544. else:
  545. href = data[start_index:index - 1]
  546. break
  547. if c != ' ':
  548. last = c
  549. # We have a scenario: [test](link"notitle)
  550. # When we enter a string, we stop tracking bracket resolution in the main counter,
  551. # but we do keep a backup counter up until we discover where we might resolve all brackets
  552. # if the title string fails to resolve.
  553. if bracket_count != 0 and backtrack_count == 0:
  554. href = data[start_index:last_bracket - 1]
  555. index = last_bracket
  556. bracket_count = 0
  557. handled = bracket_count == 0
  558. if title is not None:
  559. title = self.RE_TITLE_CLEAN.sub(' ', dequote(self.unescape(title.strip())))
  560. href = self.unescape(href).strip()
  561. return href, title, index, handled
  562. def getText(self, data, index):
  563. """Parse the content between `[]` of the start of an image or link
  564. resolving nested square brackets.
  565. """
  566. bracket_count = 1
  567. text = []
  568. for pos in range(index, len(data)):
  569. c = data[pos]
  570. if c == ']':
  571. bracket_count -= 1
  572. elif c == '[':
  573. bracket_count += 1
  574. index += 1
  575. if bracket_count == 0:
  576. break
  577. text.append(c)
  578. return ''.join(text), index, bracket_count == 0
  579. class ImageInlineProcessor(LinkInlineProcessor):
  580. """ Return a img element from the given match. """
  581. def handleMatch(self, m, data):
  582. text, index, handled = self.getText(data, m.end(0))
  583. if not handled:
  584. return None, None, None
  585. src, title, index, handled = self.getLink(data, index)
  586. if not handled:
  587. return None, None, None
  588. el = etree.Element("img")
  589. el.set("src", src)
  590. if title is not None:
  591. el.set("title", title)
  592. el.set('alt', self.unescape(text))
  593. return el, m.start(0), index
  594. class ReferenceInlineProcessor(LinkInlineProcessor):
  595. """ Match to a stored reference and return link element. """
  596. NEWLINE_CLEANUP_RE = re.compile(r'\s+', re.MULTILINE)
  597. RE_LINK = re.compile(r'\s?\[([^\]]*)\]', re.DOTALL | re.UNICODE)
  598. def handleMatch(self, m, data):
  599. text, index, handled = self.getText(data, m.end(0))
  600. if not handled:
  601. return None, None, None
  602. id, end, handled = self.evalId(data, index, text)
  603. if not handled:
  604. return None, None, None
  605. # Clean up linebreaks in id
  606. id = self.NEWLINE_CLEANUP_RE.sub(' ', id)
  607. if id not in self.md.references: # ignore undefined refs
  608. return None, m.start(0), end
  609. href, title = self.md.references[id]
  610. return self.makeTag(href, title, text), m.start(0), end
  611. def evalId(self, data, index, text):
  612. """
  613. Evaluate the id portion of [ref][id].
  614. If [ref][] use [ref].
  615. """
  616. m = self.RE_LINK.match(data, pos=index)
  617. if not m:
  618. return None, index, False
  619. else:
  620. id = m.group(1).lower()
  621. end = m.end(0)
  622. if not id:
  623. id = text.lower()
  624. return id, end, True
  625. def makeTag(self, href, title, text):
  626. el = etree.Element('a')
  627. el.set('href', href)
  628. if title:
  629. el.set('title', title)
  630. el.text = text
  631. return el
  632. class ShortReferenceInlineProcessor(ReferenceInlineProcessor):
  633. """Shorte form of reference: [google]. """
  634. def evalId(self, data, index, text):
  635. """Evaluate the id from of [ref] """
  636. return text.lower(), index, True
  637. class ImageReferenceInlineProcessor(ReferenceInlineProcessor):
  638. """ Match to a stored reference and return img element. """
  639. def makeTag(self, href, title, text):
  640. el = etree.Element("img")
  641. el.set("src", href)
  642. if title:
  643. el.set("title", title)
  644. el.set("alt", self.unescape(text))
  645. return el
  646. class AutolinkInlineProcessor(InlineProcessor):
  647. """ Return a link Element given an autolink (`<http://example/com>`). """
  648. def handleMatch(self, m, data):
  649. el = etree.Element("a")
  650. el.set('href', self.unescape(m.group(1)))
  651. el.text = util.AtomicString(m.group(1))
  652. return el, m.start(0), m.end(0)
  653. class AutomailInlineProcessor(InlineProcessor):
  654. """
  655. Return a mailto link Element given an automail link (`<foo@example.com>`).
  656. """
  657. def handleMatch(self, m, data):
  658. el = etree.Element('a')
  659. email = self.unescape(m.group(1))
  660. if email.startswith("mailto:"):
  661. email = email[len("mailto:"):]
  662. def codepoint2name(code):
  663. """Return entity definition by code, or the code if not defined."""
  664. entity = entities.codepoint2name.get(code)
  665. if entity:
  666. return "{}{};".format(util.AMP_SUBSTITUTE, entity)
  667. else:
  668. return "%s#%d;" % (util.AMP_SUBSTITUTE, code)
  669. letters = [codepoint2name(ord(letter)) for letter in email]
  670. el.text = util.AtomicString(''.join(letters))
  671. mailto = "mailto:" + email
  672. mailto = "".join([util.AMP_SUBSTITUTE + '#%d;' %
  673. ord(letter) for letter in mailto])
  674. el.set('href', mailto)
  675. return el, m.start(0), m.end(0)