markup.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. # -*- coding: utf-8 -*-
  2. """
  3. pygments.lexers.markup
  4. ~~~~~~~~~~~~~~~~~~~~~~
  5. Lexers for non-HTML markup languages.
  6. :copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS.
  7. :license: BSD, see LICENSE for details.
  8. """
  9. import re
  10. from pygments.lexers.html import HtmlLexer, XmlLexer
  11. from pygments.lexers.javascript import JavascriptLexer
  12. from pygments.lexers.css import CssLexer
  13. from pygments.lexer import RegexLexer, DelegatingLexer, include, bygroups, \
  14. using, this, do_insertions, default, words
  15. from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
  16. Number, Punctuation, Generic, Other
  17. from pygments.util import get_bool_opt, ClassNotFound
  18. __all__ = ['BBCodeLexer', 'MoinWikiLexer', 'RstLexer', 'TexLexer', 'GroffLexer',
  19. 'MozPreprocHashLexer', 'MozPreprocPercentLexer',
  20. 'MozPreprocXulLexer', 'MozPreprocJavascriptLexer',
  21. 'MozPreprocCssLexer', 'MarkdownLexer']
  22. class BBCodeLexer(RegexLexer):
  23. """
  24. A lexer that highlights BBCode(-like) syntax.
  25. .. versionadded:: 0.6
  26. """
  27. name = 'BBCode'
  28. aliases = ['bbcode']
  29. mimetypes = ['text/x-bbcode']
  30. tokens = {
  31. 'root': [
  32. (r'[^[]+', Text),
  33. # tag/end tag begin
  34. (r'\[/?\w+', Keyword, 'tag'),
  35. # stray bracket
  36. (r'\[', Text),
  37. ],
  38. 'tag': [
  39. (r'\s+', Text),
  40. # attribute with value
  41. (r'(\w+)(=)("?[^\s"\]]+"?)',
  42. bygroups(Name.Attribute, Operator, String)),
  43. # tag argument (a la [color=green])
  44. (r'(=)("?[^\s"\]]+"?)',
  45. bygroups(Operator, String)),
  46. # tag end
  47. (r'\]', Keyword, '#pop'),
  48. ],
  49. }
  50. class MoinWikiLexer(RegexLexer):
  51. """
  52. For MoinMoin (and Trac) Wiki markup.
  53. .. versionadded:: 0.7
  54. """
  55. name = 'MoinMoin/Trac Wiki markup'
  56. aliases = ['trac-wiki', 'moin']
  57. filenames = []
  58. mimetypes = ['text/x-trac-wiki']
  59. flags = re.MULTILINE | re.IGNORECASE
  60. tokens = {
  61. 'root': [
  62. (r'^#.*$', Comment),
  63. (r'(!)(\S+)', bygroups(Keyword, Text)), # Ignore-next
  64. # Titles
  65. (r'^(=+)([^=]+)(=+)(\s*#.+)?$',
  66. bygroups(Generic.Heading, using(this), Generic.Heading, String)),
  67. # Literal code blocks, with optional shebang
  68. (r'(\{\{\{)(\n#!.+)?', bygroups(Name.Builtin, Name.Namespace), 'codeblock'),
  69. (r'(\'\'\'?|\|\||`|__|~~|\^|,,|::)', Comment), # Formatting
  70. # Lists
  71. (r'^( +)([.*-])( )', bygroups(Text, Name.Builtin, Text)),
  72. (r'^( +)([a-z]{1,5}\.)( )', bygroups(Text, Name.Builtin, Text)),
  73. # Other Formatting
  74. (r'\[\[\w+.*?\]\]', Keyword), # Macro
  75. (r'(\[[^\s\]]+)(\s+[^\]]+?)?(\])',
  76. bygroups(Keyword, String, Keyword)), # Link
  77. (r'^----+$', Keyword), # Horizontal rules
  78. (r'[^\n\'\[{!_~^,|]+', Text),
  79. (r'\n', Text),
  80. (r'.', Text),
  81. ],
  82. 'codeblock': [
  83. (r'\}\}\}', Name.Builtin, '#pop'),
  84. # these blocks are allowed to be nested in Trac, but not MoinMoin
  85. (r'\{\{\{', Text, '#push'),
  86. (r'[^{}]+', Comment.Preproc), # slurp boring text
  87. (r'.', Comment.Preproc), # allow loose { or }
  88. ],
  89. }
  90. class RstLexer(RegexLexer):
  91. """
  92. For `reStructuredText <http://docutils.sf.net/rst.html>`_ markup.
  93. .. versionadded:: 0.7
  94. Additional options accepted:
  95. `handlecodeblocks`
  96. Highlight the contents of ``.. sourcecode:: language``,
  97. ``.. code:: language`` and ``.. code-block:: language``
  98. directives with a lexer for the given language (default:
  99. ``True``).
  100. .. versionadded:: 0.8
  101. """
  102. name = 'reStructuredText'
  103. aliases = ['rst', 'rest', 'restructuredtext']
  104. filenames = ['*.rst', '*.rest']
  105. mimetypes = ["text/x-rst", "text/prs.fallenstein.rst"]
  106. flags = re.MULTILINE
  107. def _handle_sourcecode(self, match):
  108. from pygments.lexers import get_lexer_by_name
  109. # section header
  110. yield match.start(1), Punctuation, match.group(1)
  111. yield match.start(2), Text, match.group(2)
  112. yield match.start(3), Operator.Word, match.group(3)
  113. yield match.start(4), Punctuation, match.group(4)
  114. yield match.start(5), Text, match.group(5)
  115. yield match.start(6), Keyword, match.group(6)
  116. yield match.start(7), Text, match.group(7)
  117. # lookup lexer if wanted and existing
  118. lexer = None
  119. if self.handlecodeblocks:
  120. try:
  121. lexer = get_lexer_by_name(match.group(6).strip())
  122. except ClassNotFound:
  123. pass
  124. indention = match.group(8)
  125. indention_size = len(indention)
  126. code = (indention + match.group(9) + match.group(10) + match.group(11))
  127. # no lexer for this language. handle it like it was a code block
  128. if lexer is None:
  129. yield match.start(8), String, code
  130. return
  131. # highlight the lines with the lexer.
  132. ins = []
  133. codelines = code.splitlines(True)
  134. code = ''
  135. for line in codelines:
  136. if len(line) > indention_size:
  137. ins.append((len(code), [(0, Text, line[:indention_size])]))
  138. code += line[indention_size:]
  139. else:
  140. code += line
  141. for item in do_insertions(ins, lexer.get_tokens_unprocessed(code)):
  142. yield item
  143. # from docutils.parsers.rst.states
  144. closers = u'\'")]}>\u2019\u201d\xbb!?'
  145. unicode_delimiters = u'\u2010\u2011\u2012\u2013\u2014\u00a0'
  146. end_string_suffix = (r'((?=$)|(?=[-/:.,; \n\x00%s%s]))'
  147. % (re.escape(unicode_delimiters),
  148. re.escape(closers)))
  149. tokens = {
  150. 'root': [
  151. # Heading with overline
  152. (r'^(=+|-+|`+|:+|\.+|\'+|"+|~+|\^+|_+|\*+|\++|#+)([ \t]*\n)'
  153. r'(.+)(\n)(\1)(\n)',
  154. bygroups(Generic.Heading, Text, Generic.Heading,
  155. Text, Generic.Heading, Text)),
  156. # Plain heading
  157. (r'^(\S.*)(\n)(={3,}|-{3,}|`{3,}|:{3,}|\.{3,}|\'{3,}|"{3,}|'
  158. r'~{3,}|\^{3,}|_{3,}|\*{3,}|\+{3,}|#{3,})(\n)',
  159. bygroups(Generic.Heading, Text, Generic.Heading, Text)),
  160. # Bulleted lists
  161. (r'^(\s*)([-*+])( .+\n(?:\1 .+\n)*)',
  162. bygroups(Text, Number, using(this, state='inline'))),
  163. # Numbered lists
  164. (r'^(\s*)([0-9#ivxlcmIVXLCM]+\.)( .+\n(?:\1 .+\n)*)',
  165. bygroups(Text, Number, using(this, state='inline'))),
  166. (r'^(\s*)(\(?[0-9#ivxlcmIVXLCM]+\))( .+\n(?:\1 .+\n)*)',
  167. bygroups(Text, Number, using(this, state='inline'))),
  168. # Numbered, but keep words at BOL from becoming lists
  169. (r'^(\s*)([A-Z]+\.)( .+\n(?:\1 .+\n)+)',
  170. bygroups(Text, Number, using(this, state='inline'))),
  171. (r'^(\s*)(\(?[A-Za-z]+\))( .+\n(?:\1 .+\n)+)',
  172. bygroups(Text, Number, using(this, state='inline'))),
  173. # Line blocks
  174. (r'^(\s*)(\|)( .+\n(?:\| .+\n)*)',
  175. bygroups(Text, Operator, using(this, state='inline'))),
  176. # Sourcecode directives
  177. (r'^( *\.\.)(\s*)((?:source)?code(?:-block)?)(::)([ \t]*)([^\n]+)'
  178. r'(\n[ \t]*\n)([ \t]+)(.*)(\n)((?:(?:\8.*|)\n)+)',
  179. _handle_sourcecode),
  180. # A directive
  181. (r'^( *\.\.)(\s*)([\w:-]+?)(::)(?:([ \t]*)(.*))',
  182. bygroups(Punctuation, Text, Operator.Word, Punctuation, Text,
  183. using(this, state='inline'))),
  184. # A reference target
  185. (r'^( *\.\.)(\s*)(_(?:[^:\\]|\\.)+:)(.*?)$',
  186. bygroups(Punctuation, Text, Name.Tag, using(this, state='inline'))),
  187. # A footnote/citation target
  188. (r'^( *\.\.)(\s*)(\[.+\])(.*?)$',
  189. bygroups(Punctuation, Text, Name.Tag, using(this, state='inline'))),
  190. # A substitution def
  191. (r'^( *\.\.)(\s*)(\|.+\|)(\s*)([\w:-]+?)(::)(?:([ \t]*)(.*))',
  192. bygroups(Punctuation, Text, Name.Tag, Text, Operator.Word,
  193. Punctuation, Text, using(this, state='inline'))),
  194. # Comments
  195. (r'^ *\.\..*(\n( +.*\n|\n)+)?', Comment.Preproc),
  196. # Field list
  197. (r'^( *)(:[a-zA-Z-]+:)(\s*)$', bygroups(Text, Name.Class, Text)),
  198. (r'^( *)(:.*?:)([ \t]+)(.*?)$',
  199. bygroups(Text, Name.Class, Text, Name.Function)),
  200. # Definition list
  201. (r'^(\S.*(?<!::)\n)((?:(?: +.*)\n)+)',
  202. bygroups(using(this, state='inline'), using(this, state='inline'))),
  203. # Code blocks
  204. (r'(::)(\n[ \t]*\n)([ \t]+)(.*)(\n)((?:(?:\3.*|)\n)+)',
  205. bygroups(String.Escape, Text, String, String, Text, String)),
  206. include('inline'),
  207. ],
  208. 'inline': [
  209. (r'\\.', Text), # escape
  210. (r'``', String, 'literal'), # code
  211. (r'(`.+?)(<.+?>)(`__?)', # reference with inline target
  212. bygroups(String, String.Interpol, String)),
  213. (r'`.+?`__?', String), # reference
  214. (r'(`.+?`)(:[a-zA-Z0-9:-]+?:)?',
  215. bygroups(Name.Variable, Name.Attribute)), # role
  216. (r'(:[a-zA-Z0-9:-]+?:)(`.+?`)',
  217. bygroups(Name.Attribute, Name.Variable)), # role (content first)
  218. (r'\*\*.+?\*\*', Generic.Strong), # Strong emphasis
  219. (r'\*.+?\*', Generic.Emph), # Emphasis
  220. (r'\[.*?\]_', String), # Footnote or citation
  221. (r'<.+?>', Name.Tag), # Hyperlink
  222. (r'[^\\\n\[*`:]+', Text),
  223. (r'.', Text),
  224. ],
  225. 'literal': [
  226. (r'[^`]+', String),
  227. (r'``' + end_string_suffix, String, '#pop'),
  228. (r'`', String),
  229. ]
  230. }
  231. def __init__(self, **options):
  232. self.handlecodeblocks = get_bool_opt(options, 'handlecodeblocks', True)
  233. RegexLexer.__init__(self, **options)
  234. def analyse_text(text):
  235. if text[:2] == '..' and text[2:3] != '.':
  236. return 0.3
  237. p1 = text.find("\n")
  238. p2 = text.find("\n", p1 + 1)
  239. if (p2 > -1 and # has two lines
  240. p1 * 2 + 1 == p2 and # they are the same length
  241. text[p1+1] in '-=' and # the next line both starts and ends with
  242. text[p1+1] == text[p2-1]): # ...a sufficiently high header
  243. return 0.5
  244. class TexLexer(RegexLexer):
  245. """
  246. Lexer for the TeX and LaTeX typesetting languages.
  247. """
  248. name = 'TeX'
  249. aliases = ['tex', 'latex']
  250. filenames = ['*.tex', '*.aux', '*.toc']
  251. mimetypes = ['text/x-tex', 'text/x-latex']
  252. tokens = {
  253. 'general': [
  254. (r'%.*?\n', Comment),
  255. (r'[{}]', Name.Builtin),
  256. (r'[&_^]', Name.Builtin),
  257. ],
  258. 'root': [
  259. (r'\\\[', String.Backtick, 'displaymath'),
  260. (r'\\\(', String, 'inlinemath'),
  261. (r'\$\$', String.Backtick, 'displaymath'),
  262. (r'\$', String, 'inlinemath'),
  263. (r'\\([a-zA-Z]+|.)', Keyword, 'command'),
  264. (r'\\$', Keyword),
  265. include('general'),
  266. (r'[^\\$%&_^{}]+', Text),
  267. ],
  268. 'math': [
  269. (r'\\([a-zA-Z]+|.)', Name.Variable),
  270. include('general'),
  271. (r'[0-9]+', Number),
  272. (r'[-=!+*/()\[\]]', Operator),
  273. (r'[^=!+*/()\[\]\\$%&_^{}0-9-]+', Name.Builtin),
  274. ],
  275. 'inlinemath': [
  276. (r'\\\)', String, '#pop'),
  277. (r'\$', String, '#pop'),
  278. include('math'),
  279. ],
  280. 'displaymath': [
  281. (r'\\\]', String, '#pop'),
  282. (r'\$\$', String, '#pop'),
  283. (r'\$', Name.Builtin),
  284. include('math'),
  285. ],
  286. 'command': [
  287. (r'\[.*?\]', Name.Attribute),
  288. (r'\*', Keyword),
  289. default('#pop'),
  290. ],
  291. }
  292. def analyse_text(text):
  293. for start in ("\\documentclass", "\\input", "\\documentstyle",
  294. "\\relax"):
  295. if text[:len(start)] == start:
  296. return True
  297. class GroffLexer(RegexLexer):
  298. """
  299. Lexer for the (g)roff typesetting language, supporting groff
  300. extensions. Mainly useful for highlighting manpage sources.
  301. .. versionadded:: 0.6
  302. """
  303. name = 'Groff'
  304. aliases = ['groff', 'nroff', 'man']
  305. filenames = ['*.[1234567]', '*.man']
  306. mimetypes = ['application/x-troff', 'text/troff']
  307. tokens = {
  308. 'root': [
  309. (r'(\.)(\w+)', bygroups(Text, Keyword), 'request'),
  310. (r'\.', Punctuation, 'request'),
  311. # Regular characters, slurp till we find a backslash or newline
  312. (r'[^\\\n]+', Text, 'textline'),
  313. default('textline'),
  314. ],
  315. 'textline': [
  316. include('escapes'),
  317. (r'[^\\\n]+', Text),
  318. (r'\n', Text, '#pop'),
  319. ],
  320. 'escapes': [
  321. # groff has many ways to write escapes.
  322. (r'\\"[^\n]*', Comment),
  323. (r'\\[fn]\w', String.Escape),
  324. (r'\\\(.{2}', String.Escape),
  325. (r'\\.\[.*\]', String.Escape),
  326. (r'\\.', String.Escape),
  327. (r'\\\n', Text, 'request'),
  328. ],
  329. 'request': [
  330. (r'\n', Text, '#pop'),
  331. include('escapes'),
  332. (r'"[^\n"]+"', String.Double),
  333. (r'\d+', Number),
  334. (r'\S+', String),
  335. (r'\s+', Text),
  336. ],
  337. }
  338. def analyse_text(text):
  339. if text[:1] != '.':
  340. return False
  341. if text[:3] == '.\\"':
  342. return True
  343. if text[:4] == '.TH ':
  344. return True
  345. if text[1:3].isalnum() and text[3].isspace():
  346. return 0.9
  347. class MozPreprocHashLexer(RegexLexer):
  348. """
  349. Lexer for Mozilla Preprocessor files (with '#' as the marker).
  350. Other data is left untouched.
  351. .. versionadded:: 2.0
  352. """
  353. name = 'mozhashpreproc'
  354. aliases = [name]
  355. filenames = []
  356. mimetypes = []
  357. tokens = {
  358. 'root': [
  359. (r'^#', Comment.Preproc, ('expr', 'exprstart')),
  360. (r'.+', Other),
  361. ],
  362. 'exprstart': [
  363. (r'(literal)(.*)', bygroups(Comment.Preproc, Text), '#pop:2'),
  364. (words((
  365. 'define', 'undef', 'if', 'ifdef', 'ifndef', 'else', 'elif',
  366. 'elifdef', 'elifndef', 'endif', 'expand', 'filter', 'unfilter',
  367. 'include', 'includesubst', 'error')),
  368. Comment.Preproc, '#pop'),
  369. ],
  370. 'expr': [
  371. (words(('!', '!=', '==', '&&', '||')), Operator),
  372. (r'(defined)(\()', bygroups(Keyword, Punctuation)),
  373. (r'\)', Punctuation),
  374. (r'[0-9]+', Number.Decimal),
  375. (r'__\w+?__', Name.Variable),
  376. (r'@\w+?@', Name.Class),
  377. (r'\w+', Name),
  378. (r'\n', Text, '#pop'),
  379. (r'\s+', Text),
  380. (r'\S', Punctuation),
  381. ],
  382. }
  383. class MozPreprocPercentLexer(MozPreprocHashLexer):
  384. """
  385. Lexer for Mozilla Preprocessor files (with '%' as the marker).
  386. Other data is left untouched.
  387. .. versionadded:: 2.0
  388. """
  389. name = 'mozpercentpreproc'
  390. aliases = [name]
  391. filenames = []
  392. mimetypes = []
  393. tokens = {
  394. 'root': [
  395. (r'^%', Comment.Preproc, ('expr', 'exprstart')),
  396. (r'.+', Other),
  397. ],
  398. }
  399. class MozPreprocXulLexer(DelegatingLexer):
  400. """
  401. Subclass of the `MozPreprocHashLexer` that highlights unlexed data with the
  402. `XmlLexer`.
  403. .. versionadded:: 2.0
  404. """
  405. name = "XUL+mozpreproc"
  406. aliases = ['xul+mozpreproc']
  407. filenames = ['*.xul.in']
  408. mimetypes = []
  409. def __init__(self, **options):
  410. super(MozPreprocXulLexer, self).__init__(
  411. XmlLexer, MozPreprocHashLexer, **options)
  412. class MozPreprocJavascriptLexer(DelegatingLexer):
  413. """
  414. Subclass of the `MozPreprocHashLexer` that highlights unlexed data with the
  415. `JavascriptLexer`.
  416. .. versionadded:: 2.0
  417. """
  418. name = "Javascript+mozpreproc"
  419. aliases = ['javascript+mozpreproc']
  420. filenames = ['*.js.in']
  421. mimetypes = []
  422. def __init__(self, **options):
  423. super(MozPreprocJavascriptLexer, self).__init__(
  424. JavascriptLexer, MozPreprocHashLexer, **options)
  425. class MozPreprocCssLexer(DelegatingLexer):
  426. """
  427. Subclass of the `MozPreprocHashLexer` that highlights unlexed data with the
  428. `CssLexer`.
  429. .. versionadded:: 2.0
  430. """
  431. name = "CSS+mozpreproc"
  432. aliases = ['css+mozpreproc']
  433. filenames = ['*.css.in']
  434. mimetypes = []
  435. def __init__(self, **options):
  436. super(MozPreprocCssLexer, self).__init__(
  437. CssLexer, MozPreprocPercentLexer, **options)
  438. class MarkdownLexer(RegexLexer):
  439. """
  440. For `Markdown <https://help.github.com/categories/writing-on-github/>`_ markup.
  441. .. versionadded:: 2.2
  442. """
  443. name = 'markdown'
  444. aliases = ['md']
  445. filenames = ['*.md']
  446. mimetypes = ["text/x-markdown"]
  447. flags = re.MULTILINE
  448. def _handle_codeblock(self, match):
  449. """
  450. match args: 1:backticks, 2:lang_name, 3:newline, 4:code, 5:backticks
  451. """
  452. from pygments.lexers import get_lexer_by_name
  453. # section header
  454. yield match.start(1), String , match.group(1)
  455. yield match.start(2), String , match.group(2)
  456. yield match.start(3), Text , match.group(3)
  457. # lookup lexer if wanted and existing
  458. lexer = None
  459. if self.handlecodeblocks:
  460. try:
  461. lexer = get_lexer_by_name( match.group(2).strip() )
  462. except ClassNotFound:
  463. pass
  464. code = match.group(4)
  465. # no lexer for this language. handle it like it was a code block
  466. if lexer is None:
  467. yield match.start(4), String, code
  468. return
  469. for item in do_insertions([], lexer.get_tokens_unprocessed(code)):
  470. yield item
  471. yield match.start(5), String , match.group(5)
  472. tokens = {
  473. 'root': [
  474. # heading with pound prefix
  475. (r'^(#)([^#].+\n)', bygroups(Generic.Heading, Text)),
  476. (r'^(#{2,6})(.+\n)', bygroups(Generic.Subheading, Text)),
  477. # task list
  478. (r'^(\s*)([*-] )(\[[ xX]\])( .+\n)',
  479. bygroups(Text, Keyword, Keyword, using(this, state='inline'))),
  480. # bulleted lists
  481. (r'^(\s*)([*-])(\s)(.+\n)',
  482. bygroups(Text, Keyword, Text, using(this, state='inline'))),
  483. # numbered lists
  484. (r'^(\s*)([0-9]+\.)( .+\n)',
  485. bygroups(Text, Keyword, using(this, state='inline'))),
  486. # quote
  487. (r'^(\s*>\s)(.+\n)', bygroups(Keyword, Generic.Emph)),
  488. # text block
  489. (r'^(```\n)([\w\W]*?)(^```$)', bygroups(String, Text, String)),
  490. # code block with language
  491. (r'^(```)(\w+)(\n)([\w\W]*?)(^```$)', _handle_codeblock),
  492. include('inline'),
  493. ],
  494. 'inline': [
  495. # escape
  496. (r'\\.', Text),
  497. # italics
  498. (r'(\s)([*_][^*_]+[*_])(\W|\n)', bygroups(Text, Generic.Emph, Text)),
  499. # bold
  500. # warning: the following rule eats internal tags. eg. **foo _bar_ baz** bar is not italics
  501. (r'(\s)((\*\*|__).*\3)((?=\W|\n))', bygroups(Text, Generic.Strong, None, Text)),
  502. # "proper way" (r'(\s)([*_]{2}[^*_]+[*_]{2})((?=\W|\n))', bygroups(Text, Generic.Strong, Text)),
  503. # strikethrough
  504. (r'(\s)(~~[^~]+~~)((?=\W|\n))', bygroups(Text, Generic.Deleted, Text)),
  505. # inline code
  506. (r'`[^`]+`', String.Backtick),
  507. # mentions and topics (twitter and github stuff)
  508. (r'[@#][\w/:]+', Name.Entity),
  509. # (image?) links eg: ![Image of Yaktocat](https://octodex.github.com/images/yaktocat.png)
  510. (r'(!?\[)([^]]+)(\])(\()([^)]+)(\))', bygroups(Text, Name.Tag, Text, Text, Name.Attribute, Text)),
  511. # general text, must come last!
  512. (r'[^\\\s]+', Text),
  513. (r'.', Text),
  514. ],
  515. }
  516. def __init__(self, **options):
  517. self.handlecodeblocks = get_bool_opt(options, 'handlecodeblocks', True)
  518. RegexLexer.__init__(self, **options)