test.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. ;(function () {
  4. var config = { tabSize: 4, indentUnit: 2 }
  5. var mode = CodeMirror.getMode(config, 'markdown')
  6. function MT(name) {
  7. test.mode(name, mode, Array.prototype.slice.call(arguments, 1))
  8. }
  9. var modeHighlightFormatting = CodeMirror.getMode(config, { name: 'markdown', highlightFormatting: true })
  10. function FT(name) {
  11. test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1))
  12. }
  13. var modeMT_noXml = CodeMirror.getMode(config, { name: 'markdown', xml: false })
  14. function MT_noXml(name) {
  15. test.mode(name, modeMT_noXml, Array.prototype.slice.call(arguments, 1))
  16. }
  17. var modeMT_noFencedHighlight = CodeMirror.getMode(config, { name: 'markdown', fencedCodeBlockHighlighting: false })
  18. function MT_noFencedHighlight(name) {
  19. test.mode(name, modeMT_noFencedHighlight, Array.prototype.slice.call(arguments, 1))
  20. }
  21. var modeAtxNoSpace = CodeMirror.getMode(config, { name: 'markdown', allowAtxHeaderWithoutSpace: true })
  22. function AtxNoSpaceTest(name) {
  23. test.mode(name, modeAtxNoSpace, Array.prototype.slice.call(arguments, 1))
  24. }
  25. var modeOverrideClasses = CodeMirror.getMode(config, {
  26. name: 'markdown',
  27. strikethrough: true,
  28. emoji: true,
  29. tokenTypeOverrides: {
  30. header: 'override-header',
  31. code: 'override-code',
  32. quote: 'override-quote',
  33. list1: 'override-list1',
  34. list2: 'override-list2',
  35. list3: 'override-list3',
  36. hr: 'override-hr',
  37. image: 'override-image',
  38. imageAltText: 'override-image-alt-text',
  39. imageMarker: 'override-image-marker',
  40. linkInline: 'override-link-inline',
  41. linkEmail: 'override-link-email',
  42. linkText: 'override-link-text',
  43. linkHref: 'override-link-href',
  44. em: 'override-em',
  45. strong: 'override-strong',
  46. strikethrough: 'override-strikethrough',
  47. emoji: 'override-emoji',
  48. },
  49. })
  50. function TokenTypeOverrideTest(name) {
  51. test.mode(name, modeOverrideClasses, Array.prototype.slice.call(arguments, 1))
  52. }
  53. var modeFormattingOverride = CodeMirror.getMode(config, {
  54. name: 'markdown',
  55. highlightFormatting: true,
  56. tokenTypeOverrides: {
  57. formatting: 'override-formatting',
  58. },
  59. })
  60. function FormatTokenTypeOverrideTest(name) {
  61. test.mode(name, modeFormattingOverride, Array.prototype.slice.call(arguments, 1))
  62. }
  63. var modeET = CodeMirror.getMode(config, { name: 'markdown', emoji: true })
  64. function ET(name) {
  65. test.mode(name, modeET, Array.prototype.slice.call(arguments, 1))
  66. }
  67. FT('formatting_emAsterisk', '[em&formatting&formatting-em *][em foo][em&formatting&formatting-em *]')
  68. FT('formatting_emUnderscore', '[em&formatting&formatting-em _][em foo][em&formatting&formatting-em _]')
  69. FT('formatting_strongAsterisk', '[strong&formatting&formatting-strong **][strong foo][strong&formatting&formatting-strong **]')
  70. FT('formatting_strongUnderscore', '[strong&formatting&formatting-strong __][strong foo][strong&formatting&formatting-strong __]')
  71. FT('formatting_codeBackticks', '[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]')
  72. FT('formatting_doubleBackticks', '[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]')
  73. FT('formatting_atxHeader', '[header&header-1&formatting&formatting-header&formatting-header-1 # ][header&header-1 foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]')
  74. FT('formatting_setextHeader', '[header&header-1 foo]', '[header&header-1&formatting&formatting-header&formatting-header-1 =]')
  75. FT('formatting_blockquote', '[quote&quote-1&formatting&formatting-quote&formatting-quote-1 > ][quote&quote-1 foo]')
  76. FT('formatting_list', '[variable-2&formatting&formatting-list&formatting-list-ul - ][variable-2 foo]')
  77. FT('formatting_list', '[variable-2&formatting&formatting-list&formatting-list-ol 1. ][variable-2 foo]')
  78. FT(
  79. 'formatting_link',
  80. '[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string&url (][string&url http://example.com/][string&formatting&formatting-link-string&url )]'
  81. )
  82. FT(
  83. 'formatting_linkReference',
  84. '[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string&url [][string&url bar][string&formatting&formatting-link-string&url ]]]',
  85. '[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string&url http://example.com/]'
  86. )
  87. FT('formatting_linkWeb', '[link&formatting&formatting-link <][link http://example.com/][link&formatting&formatting-link >]')
  88. FT('formatting_linkEmail', '[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]')
  89. FT('formatting_escape', '[formatting-escape \\*]')
  90. FT(
  91. 'formatting_image',
  92. '[formatting&formatting-image&image&image-marker !][formatting&formatting-image&image&image-alt-text&link [[][image&image-alt-text&link alt text][formatting&formatting-image&image&image-alt-text&link ]]][formatting&formatting-link-string&string&url (][url&string http://link.to/image.jpg][formatting&formatting-link-string&string&url )]'
  93. )
  94. FT('codeBlock', '[comment&formatting&formatting-code-block ```css]', '[tag foo]', '[comment&formatting&formatting-code-block ```]')
  95. MT('plainText', 'foo')
  96. // Don't style single trailing space
  97. MT('trailingSpace1', 'foo ')
  98. // Two or more trailing spaces should be styled with line break character
  99. MT('trailingSpace2', 'foo[trailing-space-a ][trailing-space-new-line ]')
  100. MT('trailingSpace3', 'foo[trailing-space-a ][trailing-space-b ][trailing-space-new-line ]')
  101. MT('trailingSpace4', 'foo[trailing-space-a ][trailing-space-b ][trailing-space-a ][trailing-space-new-line ]')
  102. // Code blocks using 4 spaces (regardless of CodeMirror.tabSize value)
  103. MT('codeBlocksUsing4Spaces', ' [comment foo]')
  104. // Code blocks using 4 spaces with internal indentation
  105. MT('codeBlocksUsing4SpacesIndentation', ' [comment bar]', ' [comment hello]', ' [comment world]', ' [comment foo]', 'bar')
  106. // Code blocks should end even after extra indented lines
  107. MT('codeBlocksWithTrailingIndentedLine', ' [comment foo]', ' [comment bar]', ' [comment baz]', ' ', 'hello')
  108. // Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value)
  109. MT('codeBlocksUsing1Tab', '\t[comment foo]')
  110. // No code blocks directly after paragraph
  111. // http://spec.commonmark.org/0.19/#example-65
  112. MT('noCodeBlocksAfterParagraph', 'Foo', ' Bar')
  113. MT('codeBlocksAfterATX', '[header&header-1 # foo]', ' [comment code]')
  114. MT('codeBlocksAfterSetext', '[header&header-2 foo]', '[header&header-2 ---]', ' [comment code]')
  115. MT('codeBlocksAfterFencedCode', '[comment ```]', '[comment foo]', '[comment ```]', ' [comment code]')
  116. // Inline code using backticks
  117. MT('inlineCodeUsingBackticks', 'foo [comment `bar`]')
  118. // Block code using single backtick (shouldn't work)
  119. MT('blockCodeSingleBacktick', '[comment `]', '[comment foo]', '[comment `]')
  120. // Unclosed backticks
  121. // Instead of simply marking as CODE, it would be nice to have an
  122. // incomplete flag for CODE, that is styled slightly different.
  123. MT('unclosedBackticks', 'foo [comment `bar]')
  124. // Per documentation: "To include a literal backtick character within a
  125. // code span, you can use multiple backticks as the opening and closing
  126. // delimiters"
  127. MT('doubleBackticks', '[comment ``foo ` bar``]')
  128. // Tests based on Dingus
  129. // http://daringfireball.net/projects/markdown/dingus
  130. //
  131. // Multiple backticks within an inline code block
  132. MT('consecutiveBackticks', '[comment `foo```bar`]')
  133. // Multiple backticks within an inline code block with a second code block
  134. MT('consecutiveBackticks', '[comment `foo```bar`] hello [comment `world`]')
  135. // Unclosed with several different groups of backticks
  136. MT('unclosedBackticks', '[comment ``foo ``` bar` hello]')
  137. // Closed with several different groups of backticks
  138. MT('closedBackticks', '[comment ``foo ``` bar` hello``] world')
  139. // info string cannot contain backtick, thus should result in inline code
  140. MT('closingFencedMarksOnSameLine', '[comment ``` code ```] foo')
  141. // atx headers
  142. // http://daringfireball.net/projects/markdown/syntax#header
  143. MT('atxH1', '[header&header-1 # foo]')
  144. MT('atxH2', '[header&header-2 ## foo]')
  145. MT('atxH3', '[header&header-3 ### foo]')
  146. MT('atxH4', '[header&header-4 #### foo]')
  147. MT('atxH5', '[header&header-5 ##### foo]')
  148. MT('atxH6', '[header&header-6 ###### foo]')
  149. // http://spec.commonmark.org/0.19/#example-24
  150. MT('noAtxH7', '####### foo')
  151. // http://spec.commonmark.org/0.19/#example-25
  152. MT('noAtxH1WithoutSpace', '#5 bolt')
  153. // CommonMark requires a space after # but most parsers don't
  154. AtxNoSpaceTest('atxNoSpaceAllowed_H1NoSpace', '[header&header-1 #foo]')
  155. AtxNoSpaceTest('atxNoSpaceAllowed_H4NoSpace', '[header&header-4 ####foo]')
  156. AtxNoSpaceTest('atxNoSpaceAllowed_H1Space', '[header&header-1 # foo]')
  157. // Inline styles should be parsed inside headers
  158. MT('atxH1inline', '[header&header-1 # foo ][header&header-1&em *bar*]')
  159. MT('atxIndentedTooMuch', '[header&header-1 # foo]', ' [comment # bar]')
  160. // disable atx inside blockquote until we implement proper blockquote inner mode
  161. // TODO: fix to be CommonMark-compliant
  162. MT('atxNestedInsideBlockquote', '[quote&quote-1 > # foo]')
  163. MT('atxAfterBlockquote', '[quote&quote-1 > foo]', '[header&header-1 # bar]')
  164. // Setext headers - H1, H2
  165. // Per documentation, "Any number of underlining =’s or -’s will work."
  166. // http://daringfireball.net/projects/markdown/syntax#header
  167. // Ideally, the text would be marked as `header` as well, but this is
  168. // not really feasible at the moment. So, instead, we're testing against
  169. // what works today, to avoid any regressions.
  170. //
  171. // Check if single underlining = works
  172. MT('setextH1', '[header&header-1 foo]', '[header&header-1 =]')
  173. // Check if 3+ ='s work
  174. MT('setextH1', '[header&header-1 foo]', '[header&header-1 ===]')
  175. // Check if single underlining - should not be interpreted
  176. // as it might lead to an empty list:
  177. // https://spec.commonmark.org/0.28/#setext-heading-underline
  178. MT('setextH2Single', 'foo', '-')
  179. // Check if 3+ -'s work
  180. MT('setextH2', '[header&header-2 foo]', '[header&header-2 ---]')
  181. // http://spec.commonmark.org/0.19/#example-45
  182. MT('setextH2AllowSpaces', '[header&header-2 foo]', ' [header&header-2 ---- ]')
  183. // http://spec.commonmark.org/0.19/#example-44
  184. MT('noSetextAfterIndentedCodeBlock', ' [comment foo]', '[hr ---]')
  185. MT('setextAfterFencedCode', '[comment ```]', '[comment foo]', '[comment ```]', '[header&header-2 bar]', '[header&header-2 ---]')
  186. MT('setextAfterATX', '[header&header-1 # foo]', '[header&header-2 bar]', '[header&header-2 ---]')
  187. // http://spec.commonmark.org/0.19/#example-51
  188. MT('noSetextAfterQuote', '[quote&quote-1 > foo]', '[hr ---]', '', '[quote&quote-1 > foo]', '[quote&quote-1 bar]', '[hr ---]')
  189. MT('noSetextAfterList', '[variable-2 - foo]', '[hr ---]')
  190. MT('noSetextAfterList_listContinuation', '[variable-2 - foo]', 'bar', '[hr ---]')
  191. MT('setextAfterList_afterIndentedCode', '[variable-2 - foo]', '', ' [comment bar]', '[header&header-2 baz]', '[header&header-2 ---]')
  192. MT('setextAfterList_afterFencedCodeBlocks', '[variable-2 - foo]', '', ' [comment ```]', ' [comment bar]', ' [comment ```]', '[header&header-2 baz]', '[header&header-2 ---]')
  193. MT('setextAfterList_afterHeader', '[variable-2 - foo]', ' [variable-2&header&header-1 # bar]', '[header&header-2 baz]', '[header&header-2 ---]')
  194. MT('setextAfterList_afterHr', '[variable-2 - foo]', '', ' [hr ---]', '[header&header-2 bar]', '[header&header-2 ---]')
  195. MT('setext_nestedInlineMarkup', '[header&header-1 foo ][em&header&header-1 *bar*]', '[header&header-1 =]')
  196. MT('setext_linkDef', "[link [[aaa]]:] [string&url http://google.com 'title']", '[hr ---]')
  197. // currently, looks max one line ahead, thus won't catch valid CommonMark
  198. // markup
  199. MT('setext_oneLineLookahead', 'foo', '[header&header-1 bar]', '[header&header-1 =]')
  200. // ensure we regard space after a single dash as a list
  201. MT('setext_emptyList', 'foo', '[variable-2 - ]', 'foo')
  202. // Single-line blockquote with trailing space
  203. MT('blockquoteSpace', '[quote&quote-1 > foo]')
  204. // Single-line blockquote
  205. MT('blockquoteNoSpace', '[quote&quote-1 >foo]')
  206. // No blank line before blockquote
  207. MT('blockquoteNoBlankLine', 'foo', '[quote&quote-1 > bar]')
  208. MT('blockquoteNested', '[quote&quote-1 > foo]', '[quote&quote-1 >][quote&quote-2 > foo]', '[quote&quote-1 >][quote&quote-2 >][quote&quote-3 > foo]')
  209. // ensure quote-level is inferred correctly even if indented
  210. MT('blockquoteNestedIndented', ' [quote&quote-1 > foo]', ' [quote&quote-1 >][quote&quote-2 > foo]', ' [quote&quote-1 >][quote&quote-2 >][quote&quote-3 > foo]')
  211. // ensure quote-level is inferred correctly even if indented
  212. MT('blockquoteIndentedTooMuch', 'foo', ' > bar')
  213. // Single-line blockquote followed by normal paragraph
  214. MT('blockquoteThenParagraph', '[quote&quote-1 >foo]', '', 'bar')
  215. // Multi-line blockquote (lazy mode)
  216. MT('multiBlockquoteLazy', '[quote&quote-1 >foo]', '[quote&quote-1 bar]')
  217. // Multi-line blockquote followed by normal paragraph (lazy mode)
  218. MT('multiBlockquoteLazyThenParagraph', '[quote&quote-1 >foo]', '[quote&quote-1 bar]', '', 'hello')
  219. // Multi-line blockquote (non-lazy mode)
  220. MT('multiBlockquote', '[quote&quote-1 >foo]', '[quote&quote-1 >bar]')
  221. // Multi-line blockquote followed by normal paragraph (non-lazy mode)
  222. MT('multiBlockquoteThenParagraph', '[quote&quote-1 >foo]', '[quote&quote-1 >bar]', '', 'hello')
  223. // disallow lists inside blockquote for now because it causes problems outside blockquote
  224. // TODO: fix to be CommonMark-compliant
  225. MT('listNestedInBlockquote', '[quote&quote-1 > - foo]')
  226. // disallow fenced blocks inside blockquote because it causes problems outside blockquote
  227. // TODO: fix to be CommonMark-compliant
  228. MT(
  229. 'fencedBlockNestedInBlockquote',
  230. '[quote&quote-1 > ```]',
  231. '[quote&quote-1 > code]',
  232. '[quote&quote-1 > ```]',
  233. // ensure we still allow inline code
  234. '[quote&quote-1 > ][quote&quote-1&comment `code`]'
  235. )
  236. // Header with leading space after continued blockquote (#3287, negative indentation)
  237. MT('headerAfterContinuedBlockquote', '[quote&quote-1 > foo]', '[quote&quote-1 bar]', '', ' [header&header-1 # hello]')
  238. // Check list types
  239. MT('listAsterisk', 'foo', 'bar', '', '[variable-2 * foo]', '[variable-2 * bar]')
  240. MT('listPlus', 'foo', 'bar', '', '[variable-2 + foo]', '[variable-2 + bar]')
  241. MT('listDash', 'foo', 'bar', '', '[variable-2 - foo]', '[variable-2 - bar]')
  242. MT('listNumber', 'foo', 'bar', '', '[variable-2 1. foo]', '[variable-2 2. bar]')
  243. MT('listFromParagraph', 'foo', '[variable-2 1. bar]', '[variable-2 2. hello]')
  244. // List after hr
  245. MT('listAfterHr', '[hr ---]', '[variable-2 - bar]')
  246. // List after header
  247. MT('listAfterHeader', '[header&header-1 # foo]', '[variable-2 - bar]')
  248. // hr after list
  249. MT('hrAfterList', '[variable-2 - foo]', '[hr -----]')
  250. MT('hrAfterFencedCode', '[comment ```]', '[comment code]', '[comment ```]', '[hr ---]')
  251. // allow hr inside lists
  252. // (require prev line to be empty or hr, TODO: non-CommonMark-compliant)
  253. MT('hrInsideList', '[variable-2 - foo]', '', ' [hr ---]', ' [hr ---]', '', ' [comment ---]')
  254. MT('consecutiveHr', '[hr ---]', '[hr ---]', '[hr ---]')
  255. // Formatting in lists (*)
  256. MT(
  257. 'listAsteriskFormatting',
  258. '[variable-2 * ][variable-2&em *foo*][variable-2 bar]',
  259. '[variable-2 * ][variable-2&strong **foo**][variable-2 bar]',
  260. '[variable-2 * ][variable-2&em&strong ***foo***][variable-2 bar]',
  261. '[variable-2 * ][variable-2&comment `foo`][variable-2 bar]'
  262. )
  263. // Formatting in lists (+)
  264. MT(
  265. 'listPlusFormatting',
  266. '[variable-2 + ][variable-2&em *foo*][variable-2 bar]',
  267. '[variable-2 + ][variable-2&strong **foo**][variable-2 bar]',
  268. '[variable-2 + ][variable-2&em&strong ***foo***][variable-2 bar]',
  269. '[variable-2 + ][variable-2&comment `foo`][variable-2 bar]'
  270. )
  271. // Formatting in lists (-)
  272. MT(
  273. 'listDashFormatting',
  274. '[variable-2 - ][variable-2&em *foo*][variable-2 bar]',
  275. '[variable-2 - ][variable-2&strong **foo**][variable-2 bar]',
  276. '[variable-2 - ][variable-2&em&strong ***foo***][variable-2 bar]',
  277. '[variable-2 - ][variable-2&comment `foo`][variable-2 bar]'
  278. )
  279. // Formatting in lists (1.)
  280. MT(
  281. 'listNumberFormatting',
  282. '[variable-2 1. ][variable-2&em *foo*][variable-2 bar]',
  283. '[variable-2 2. ][variable-2&strong **foo**][variable-2 bar]',
  284. '[variable-2 3. ][variable-2&em&strong ***foo***][variable-2 bar]',
  285. '[variable-2 4. ][variable-2&comment `foo`][variable-2 bar]'
  286. )
  287. // Paragraph lists
  288. MT('listParagraph', '[variable-2 * foo]', '', '[variable-2 * bar]')
  289. // Multi-paragraph lists
  290. //
  291. // 4 spaces
  292. MT('listMultiParagraph', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2 hello]')
  293. // 4 spaces, extra blank lines (should still be list, per Dingus)
  294. MT('listMultiParagraphExtra', '[variable-2 * foo]', '', '[variable-2 * bar]', '', '', ' [variable-2 hello]')
  295. // 4 spaces, plus 1 space (should still be list, per Dingus)
  296. MT('listMultiParagraphExtraSpace', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2 hello]', '', ' [variable-2 world]')
  297. // 1 tab
  298. MT('listTab', '[variable-2 * foo]', '', '[variable-2 * bar]', '', '\t[variable-2 hello]')
  299. // No indent
  300. MT('listNoIndent', '[variable-2 * foo]', '', '[variable-2 * bar]', '', 'hello')
  301. MT(
  302. 'listCommonMarkIndentationCode',
  303. '[variable-2 * Code blocks also affect]',
  304. ' [variable-3 * The next level starts where the contents start.]',
  305. ' [variable-3 * Anything less than that will keep the item on the same level.]',
  306. ' [variable-3 * Each list item can indent the first level further and further.]',
  307. ' [variable-3 * For the most part, this makes sense while writing a list.]',
  308. ' [keyword * This means two items with same indentation can be different levels.]',
  309. ' [keyword * Each level has an indent requirement that can change between items.]',
  310. ' [keyword * A list item that meets this will be part of the next level.]',
  311. ' [variable-3 * Otherwise, it will be part of the level where it does meet this.]',
  312. ' [variable-2 * World]'
  313. )
  314. // should handle nested and un-nested lists
  315. MT(
  316. 'listCommonMark_MixedIndents',
  317. '[variable-2 * list1]',
  318. ' [variable-2 list1]',
  319. ' [variable-2&header&header-1 # heading still part of list1]',
  320. ' [variable-2 text after heading still part of list1]',
  321. '',
  322. ' [comment indented codeblock]',
  323. ' [variable-2 list1 after code block]',
  324. ' [variable-3 * list2]',
  325. // amount of spaces on empty lines between lists doesn't matter
  326. ' ',
  327. // extra empty lines irrelevant
  328. '',
  329. '',
  330. ' [variable-3 indented text part of list2]',
  331. ' [keyword * list3]',
  332. '',
  333. ' [variable-3 text at level of list2]',
  334. '',
  335. ' [variable-2 de-indented text part of list1 again]',
  336. '',
  337. ' [variable-2&comment ```]',
  338. ' [comment code]',
  339. ' [variable-2&comment ```]',
  340. '',
  341. ' [variable-2 text after fenced code]'
  342. )
  343. // should correctly parse numbered list content indentation
  344. MT(
  345. 'listCommonMark_NumberedListIndent',
  346. '[variable-2 1000. list with base indent of 6]',
  347. '',
  348. ' [variable-2 text must be indented 6 spaces at minimum]',
  349. '',
  350. ' [variable-2 9-spaces indented text still part of list]',
  351. '',
  352. ' [comment indented codeblock starts at 10 spaces]',
  353. '',
  354. ' [comment text indented by 5 spaces no longer belong to list]'
  355. )
  356. // should consider tab as 4 spaces
  357. MT('listCommonMark_TabIndented', '[variable-2 * list]', '\t[variable-3 * list2]', '', '\t\t[variable-3 part of list2]')
  358. MT('listAfterBlockquote', '[quote&quote-1 > foo]', '[variable-2 - bar]')
  359. // shouldn't create sublist if it's indented more than allowed
  360. MT('nestedListIndentedTooMuch', '[variable-2 - foo]', ' [variable-2 - bar]')
  361. MT('listIndentedTooMuchAfterParagraph', 'foo', ' - bar')
  362. // Blockquote
  363. MT('blockquote', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2&quote&quote-1 > hello]')
  364. // Code block
  365. MT('blockquoteCode', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [comment > hello]', '', ' [variable-2 world]')
  366. // Code block followed by text
  367. MT('blockquoteCodeText', '[variable-2 * foo]', '', ' [variable-2 bar]', '', ' [comment hello]', '', ' [variable-2 world]')
  368. // Nested list
  369. MT('listAsteriskNested', '[variable-2 * foo]', '', ' [variable-3 * bar]')
  370. MT('listPlusNested', '[variable-2 + foo]', '', ' [variable-3 + bar]')
  371. MT('listDashNested', '[variable-2 - foo]', '', ' [variable-3 - bar]')
  372. MT('listNumberNested', '[variable-2 1. foo]', '', ' [variable-3 2. bar]')
  373. MT('listMixed', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [keyword - hello]', '', ' [variable-2 1. world]')
  374. MT('listBlockquote', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [quote&quote-1&variable-3 > hello]')
  375. MT('listCode', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [comment hello]')
  376. // Code with internal indentation
  377. MT('listCodeIndentation', '[variable-2 * foo]', '', ' [comment bar]', ' [comment hello]', ' [comment world]', ' [comment foo]', ' [variable-2 bar]')
  378. // List nesting edge cases
  379. MT('listNested', '[variable-2 * foo]', '', ' [variable-3 * bar]', '', ' [variable-3 hello]')
  380. MT('listNested', '[variable-2 * foo]', '', ' [variable-3 * bar]', '', ' [keyword * foo]')
  381. // Code followed by text
  382. MT('listCodeText', '[variable-2 * foo]', '', ' [comment bar]', '', 'hello')
  383. // Following tests directly from official Markdown documentation
  384. // http://daringfireball.net/projects/markdown/syntax#hr
  385. MT('hrSpace', '[hr * * *]')
  386. MT('hr', '[hr ***]')
  387. MT('hrLong', '[hr *****]')
  388. MT('hrSpaceDash', '[hr - - -]')
  389. MT('hrDashLong', '[hr ---------------------------------------]')
  390. //Images
  391. MT('Images', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)]')
  392. //Images with highlight alt text
  393. MT('imageEm', '[image&image-marker !][image&image-alt-text&link [[][image-alt-text&em&image&link *alt text*][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]')
  394. MT('imageStrong', '[image&image-marker !][image&image-alt-text&link [[][image-alt-text&strong&image&link **alt text**][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]')
  395. MT('imageEmStrong', '[image&image-marker !][image&image-alt-text&link [[][image&image-alt-text&em&strong&link ***alt text***][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]')
  396. // Inline link with title
  397. MT('linkTitle', '[link [[foo]]][string&url (http://example.com/ "bar")] hello')
  398. // Inline link without title
  399. MT('linkNoTitle', '[link [[foo]]][string&url (http://example.com/)] bar')
  400. // Inline link with image
  401. MT('linkImage', '[link [[][link&image&image-marker !][link&image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)][link ]]][string&url (http://example.com/)] bar')
  402. // Inline link with Em
  403. MT('linkEm', '[link [[][link&em *foo*][link ]]][string&url (http://example.com/)] bar')
  404. // Inline link with Strong
  405. MT('linkStrong', '[link [[][link&strong **foo**][link ]]][string&url (http://example.com/)] bar')
  406. // Inline link with EmStrong
  407. MT('linkEmStrong', '[link [[][link&em&strong ***foo***][link ]]][string&url (http://example.com/)] bar')
  408. MT('multilineLink', '[link [[foo]', '[link bar]]][string&url (https://foo#_a)]', 'should not be italics')
  409. // Image with title
  410. MT('imageTitle', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/ "bar")] hello')
  411. // Image without title
  412. MT('imageNoTitle', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/)] bar')
  413. // Image with asterisks
  414. MT('imageAsterisks', '[image&image-marker !][image&image-alt-text&link [[ ][image&image-alt-text&em&link *alt text*][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)] bar')
  415. // Not a link. Should be normal text due to square brackets being used
  416. // regularly in text, especially in quoted material, and no space is allowed
  417. // between square brackets and parentheses (per Dingus).
  418. MT('notALink', '[link [[foo]]] (bar)')
  419. // Reference-style links
  420. MT('linkReference', '[link [[foo]]][string&url [[bar]]] hello')
  421. // Reference-style links with Em
  422. MT('linkReferenceEm', '[link [[][link&em *foo*][link ]]][string&url [[bar]]] hello')
  423. // Reference-style links with Strong
  424. MT('linkReferenceStrong', '[link [[][link&strong **foo**][link ]]][string&url [[bar]]] hello')
  425. // Reference-style links with EmStrong
  426. MT('linkReferenceEmStrong', '[link [[][link&em&strong ***foo***][link ]]][string&url [[bar]]] hello')
  427. // Reference-style links with optional space separator (per documentation)
  428. // "You can optionally use a space to separate the sets of brackets"
  429. MT('linkReferenceSpace', '[link [[foo]]] [string&url [[bar]]] hello')
  430. // Should only allow a single space ("...use *a* space...")
  431. MT('linkReferenceDoubleSpace', '[link [[foo]]] [link [[bar]]] hello')
  432. // Reference-style links with implicit link name
  433. MT('linkImplicit', '[link [[foo]]][string&url [[]]] hello')
  434. // @todo It would be nice if, at some point, the document was actually
  435. // checked to see if the referenced link exists
  436. // Link label, for reference-style links (taken from documentation)
  437. MT('labelNoTitle', '[link [[foo]]:] [string&url http://example.com/]')
  438. MT('labelIndented', ' [link [[foo]]:] [string&url http://example.com/]')
  439. MT('labelSpaceTitle', '[link [[foo bar]]:] [string&url http://example.com/ "hello"]')
  440. MT('labelDoubleTitle', '[link [[foo bar]]:] [string&url http://example.com/ "hello"] "world"')
  441. MT('labelTitleDoubleQuotes', '[link [[foo]]:] [string&url http://example.com/ "bar"]')
  442. MT('labelTitleSingleQuotes', "[link [[foo]]:] [string&url http://example.com/ 'bar']")
  443. MT('labelTitleParentheses', '[link [[foo]]:] [string&url http://example.com/ (bar)]')
  444. MT('labelTitleInvalid', '[link [[foo]]:] [string&url http://example.com/] bar')
  445. MT('labelLinkAngleBrackets', '[link [[foo]]:] [string&url <http://example.com/> "bar"]')
  446. MT('labelTitleNextDoubleQuotes', '[link [[foo]]:] [string&url http://example.com/]', '[string "bar"] hello')
  447. MT('labelTitleNextSingleQuotes', '[link [[foo]]:] [string&url http://example.com/]', "[string 'bar'] hello")
  448. MT('labelTitleNextParentheses', '[link [[foo]]:] [string&url http://example.com/]', '[string (bar)] hello')
  449. MT('labelTitleNextMixed', '[link [[foo]]:] [string&url http://example.com/]', '(bar" hello')
  450. MT('labelEscape', '[link [[foo \\]] ]]:] [string&url http://example.com/]')
  451. MT('labelEscapeColon', '[link [[foo \\]]: bar]]:] [string&url http://example.com/]')
  452. MT('labelEscapeEnd', '\\[[foo\\]]: http://example.com/')
  453. MT('linkWeb', '[link <http://example.com/>] foo')
  454. MT('linkWebDouble', '[link <http://example.com/>] foo [link <http://example.com/>]')
  455. MT('linkEmail', '[link <user@example.com>] foo')
  456. MT('linkEmailDouble', '[link <user@example.com>] foo [link <user@example.com>]')
  457. MT('emAsterisk', '[em *foo*] bar')
  458. MT('emUnderscore', '[em _foo_] bar')
  459. MT('emInWordAsterisk', 'foo[em *bar*]hello')
  460. MT('emInWordUnderscore', 'foo_bar_hello')
  461. // Per documentation: "...surround an * or _ with spaces, it’ll be
  462. // treated as a literal asterisk or underscore."
  463. MT('emEscapedBySpaceIn', 'foo [em _bar _ hello_] world')
  464. MT('emEscapedBySpaceOut', 'foo _ bar [em _hello_] world')
  465. MT('emEscapedByNewline', 'foo', '_ bar [em _hello_] world')
  466. // Unclosed emphasis characters
  467. // Instead of simply marking as EM / STRONG, it would be nice to have an
  468. // incomplete flag for EM and STRONG, that is styled slightly different.
  469. MT('emIncompleteAsterisk', 'foo [em *bar]')
  470. MT('emIncompleteUnderscore', 'foo [em _bar]')
  471. MT('strongAsterisk', '[strong **foo**] bar')
  472. MT('strongUnderscore', '[strong __foo__] bar')
  473. MT('emStrongAsterisk', '[em *foo][em&strong **bar*][strong hello**] world')
  474. MT('emStrongUnderscore', '[em _foo ][em&strong __bar_][strong hello__] world')
  475. // "...same character must be used to open and close an emphasis span.""
  476. MT('emStrongMixed', '[em _foo][em&strong **bar*hello__ world]')
  477. MT('emStrongMixed', '[em *foo ][em&strong __bar_hello** world]')
  478. MT('linkWithNestedParens', '[link [[foo]]][string&url (bar(baz))]')
  479. // These characters should be escaped:
  480. // \ backslash
  481. // ` backtick
  482. // * asterisk
  483. // _ underscore
  484. // {} curly braces
  485. // [] square brackets
  486. // () parentheses
  487. // # hash mark
  488. // + plus sign
  489. // - minus sign (hyphen)
  490. // . dot
  491. // ! exclamation mark
  492. MT('escapeBacktick', 'foo \\`bar\\`')
  493. MT('doubleEscapeBacktick', 'foo \\\\[comment `bar\\\\`]')
  494. MT('escapeAsterisk', 'foo \\*bar\\*')
  495. MT('doubleEscapeAsterisk', 'foo \\\\[em *bar\\\\*]')
  496. MT('escapeUnderscore', 'foo \\_bar\\_')
  497. MT('doubleEscapeUnderscore', 'foo \\\\[em _bar\\\\_]')
  498. MT('escapeHash', '\\# foo')
  499. MT('doubleEscapeHash', '\\\\# foo')
  500. MT('escapeNewline', '\\', '[em *foo*]')
  501. // Class override tests
  502. TokenTypeOverrideTest('overrideHeader1', '[override-header&override-header-1 # Foo]')
  503. TokenTypeOverrideTest('overrideHeader2', '[override-header&override-header-2 ## Foo]')
  504. TokenTypeOverrideTest('overrideHeader3', '[override-header&override-header-3 ### Foo]')
  505. TokenTypeOverrideTest('overrideHeader4', '[override-header&override-header-4 #### Foo]')
  506. TokenTypeOverrideTest('overrideHeader5', '[override-header&override-header-5 ##### Foo]')
  507. TokenTypeOverrideTest('overrideHeader6', '[override-header&override-header-6 ###### Foo]')
  508. TokenTypeOverrideTest('overrideCode', '[override-code `foo`]')
  509. TokenTypeOverrideTest('overrideCodeBlock', '[override-code ```]', '[override-code foo]', '[override-code ```]')
  510. TokenTypeOverrideTest('overrideQuote', '[override-quote&override-quote-1 > foo]', '[override-quote&override-quote-1 > bar]')
  511. TokenTypeOverrideTest(
  512. 'overrideQuoteNested',
  513. '[override-quote&override-quote-1 > foo]',
  514. '[override-quote&override-quote-1 >][override-quote&override-quote-2 > bar]',
  515. '[override-quote&override-quote-1 >][override-quote&override-quote-2 >][override-quote&override-quote-3 > baz]'
  516. )
  517. TokenTypeOverrideTest(
  518. 'overrideLists',
  519. '[override-list1 - foo]',
  520. '',
  521. ' [override-list2 + bar]',
  522. '',
  523. ' [override-list3 * baz]',
  524. '',
  525. ' [override-list1 1. qux]',
  526. '',
  527. ' [override-list2 - quux]'
  528. )
  529. TokenTypeOverrideTest('overrideHr', '[override-hr * * *]')
  530. TokenTypeOverrideTest('overrideImage', '[override-image&override-image-marker !][override-image&override-image-alt-text&link [[alt text]]][override-link-href&url (http://link.to/image.jpg)]')
  531. TokenTypeOverrideTest('overrideLinkText', '[override-link-text [[foo]]][override-link-href&url (http://example.com)]')
  532. TokenTypeOverrideTest('overrideLinkEmailAndInline', '[override-link-email <][override-link-inline foo@example.com>]')
  533. TokenTypeOverrideTest('overrideEm', '[override-em *foo*]')
  534. TokenTypeOverrideTest('overrideStrong', '[override-strong **foo**]')
  535. TokenTypeOverrideTest('overrideStrikethrough', '[override-strikethrough ~~foo~~]')
  536. TokenTypeOverrideTest('overrideEmoji', '[override-emoji :foo:]')
  537. FormatTokenTypeOverrideTest('overrideFormatting', '[override-formatting-escape \\*]')
  538. // Tests to make sure GFM-specific things aren't getting through
  539. MT('taskList', '[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]')
  540. MT('fencedCodeBlocks', '[comment ```]', '[comment foo]', '', '[comment bar]', '[comment ```]', 'baz')
  541. MT('fencedCodeBlocks_invalidClosingFence_trailingText', '[comment ```]', '[comment foo]', '[comment ``` must not have trailing text]', '[comment baz]')
  542. MT('fencedCodeBlocks_invalidClosingFence_trailingTabs', '[comment ```]', '[comment foo]', '[comment ```\t]', '[comment baz]')
  543. MT(
  544. 'fencedCodeBlocks_validClosingFence',
  545. '[comment ```]',
  546. '[comment foo]',
  547. // may have trailing spaces
  548. '[comment ``` ]',
  549. 'baz'
  550. )
  551. MT('fencedCodeBlocksInList_closingFenceIndented', '[variable-2 - list]', ' [variable-2&comment ```]', ' [comment foo]', ' [variable-2&comment ```]', ' [variable-2 baz]')
  552. MT('fencedCodeBlocksInList_closingFenceIndentedTooMuch', '[variable-2 - list]', ' [variable-2&comment ```]', ' [comment foo]', ' [comment ```]', ' [comment baz]')
  553. MT('fencedCodeBlockModeSwitching', '[comment ```javascript]', '[variable foo]', '', '[comment ```]', 'bar')
  554. MT_noFencedHighlight('fencedCodeBlock_noHighlight', '[comment ```javascript]', '[comment foo]', '[comment ```]')
  555. MT('fencedCodeBlockModeSwitchingObjc', '[comment ```objective-c]', '[keyword @property] [variable NSString] [operator *] [variable foo];', '[comment ```]', 'bar')
  556. MT('fencedCodeBlocksMultipleChars', '[comment `````]', '[comment foo]', '[comment ```]', '[comment foo]', '[comment `````]', 'bar')
  557. MT('fencedCodeBlocksTildes', '[comment ~~~]', '[comment foo]', '[comment ~~~]', 'bar')
  558. MT('fencedCodeBlocksTildesMultipleChars', '[comment ~~~~~]', '[comment ~~~]', '[comment foo]', '[comment ~~~~~]', 'bar')
  559. MT('fencedCodeBlocksMultipleChars', '[comment `````]', '[comment foo]', '[comment ```]', '[comment foo]', '[comment `````]', 'bar')
  560. MT('fencedCodeBlocksMixed', '[comment ~~~]', '[comment ```]', '[comment foo]', '[comment ~~~]', 'bar')
  561. MT('fencedCodeBlocksAfterBlockquote', '[quote&quote-1 > foo]', '[comment ```]', '[comment bar]', '[comment ```]')
  562. // fencedCode indented too much should act as simple indentedCode
  563. // (hence has no highlight formatting)
  564. FT('tooMuchIndentedFencedCode', ' [comment ```]', ' [comment code]', ' [comment ```]')
  565. MT(
  566. 'autoTerminateFencedCodeWhenLeavingList',
  567. '[variable-2 - list1]',
  568. ' [variable-3 - list2]',
  569. ' [variable-3&comment ```]',
  570. ' [comment code]',
  571. ' [variable-3 - list2]',
  572. ' [variable-2&comment ```]',
  573. ' [comment code]',
  574. '[quote&quote-1 > foo]'
  575. )
  576. // Tests that require XML mode
  577. MT(
  578. 'xmlMode',
  579. '[tag&bracket <][tag div][tag&bracket >]',
  580. ' *foo*',
  581. ' [tag&bracket <][tag http://github.com][tag&bracket />]',
  582. '[tag&bracket </][tag div][tag&bracket >]',
  583. '[link <http://github.com/>]'
  584. )
  585. MT(
  586. 'xmlModeWithMarkdownInside',
  587. '[tag&bracket <][tag div] [attribute markdown]=[string 1][tag&bracket >]',
  588. '[em *foo*]',
  589. '[link <http://github.com/>]',
  590. '[tag </div>]',
  591. '[link <http://github.com/>]',
  592. '[tag&bracket <][tag div][tag&bracket >]',
  593. '[tag&bracket </][tag div][tag&bracket >]'
  594. )
  595. MT('xmlModeLineBreakInTags', '[tag&bracket <][tag div] [attribute id]=[string "1"]', ' [attribute class]=[string "sth"][tag&bracket >]xxx', '[tag&bracket </][tag div][tag&bracket >]')
  596. MT('xmlModeCommentWithBlankLine', '[comment <!-- Hello]', '', '[comment World -->]')
  597. MT('xmlModeCDATA', '[atom <![CDATA[ Hello]', '', '[atom FooBar]', '[atom Test ]]]]>]')
  598. MT('xmlModePreprocessor', "[meta <?php] [meta echo '1234'; ?>]")
  599. MT_noXml('xmlHighlightDisabled', '<div>foo</div>')
  600. // Tests Emojis
  601. ET('emojiDefault', '[builtin :foobar:]')
  602. ET('emojiTable', ' :--:')
  603. })()