scripts/CodeMirror/mode/markdown/test.js (view raw)
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: https://codemirror.net/LICENSE
3
4(function() {
5 var config = {tabSize: 4, indentUnit: 2}
6 var mode = CodeMirror.getMode(config, "markdown");
7 function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
8 var modeHighlightFormatting = CodeMirror.getMode(config, {name: "markdown", highlightFormatting: true});
9 function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); }
10 var modeMT_noXml = CodeMirror.getMode(config, {name: "markdown", xml: false});
11 function MT_noXml(name) { test.mode(name, modeMT_noXml, Array.prototype.slice.call(arguments, 1)); }
12 var modeMT_noFencedHighlight = CodeMirror.getMode(config, {name: "markdown", fencedCodeBlockHighlighting: false});
13 function MT_noFencedHighlight(name) { test.mode(name, modeMT_noFencedHighlight, Array.prototype.slice.call(arguments, 1)); }
14 var modeAtxNoSpace = CodeMirror.getMode(config, {name: "markdown", allowAtxHeaderWithoutSpace: true});
15 function AtxNoSpaceTest(name) { test.mode(name, modeAtxNoSpace, Array.prototype.slice.call(arguments, 1)); }
16 var modeOverrideClasses = CodeMirror.getMode(config, {
17 name: "markdown",
18 strikethrough: true,
19 emoji: true,
20 tokenTypeOverrides: {
21 "header" : "override-header",
22 "code" : "override-code",
23 "quote" : "override-quote",
24 "list1" : "override-list1",
25 "list2" : "override-list2",
26 "list3" : "override-list3",
27 "hr" : "override-hr",
28 "image" : "override-image",
29 "imageAltText": "override-image-alt-text",
30 "imageMarker": "override-image-marker",
31 "linkInline" : "override-link-inline",
32 "linkEmail" : "override-link-email",
33 "linkText" : "override-link-text",
34 "linkHref" : "override-link-href",
35 "em" : "override-em",
36 "strong" : "override-strong",
37 "strikethrough" : "override-strikethrough",
38 "emoji" : "override-emoji"
39 }});
40 function TokenTypeOverrideTest(name) { test.mode(name, modeOverrideClasses, Array.prototype.slice.call(arguments, 1)); }
41 var modeFormattingOverride = CodeMirror.getMode(config, {
42 name: "markdown",
43 highlightFormatting: true,
44 tokenTypeOverrides: {
45 "formatting" : "override-formatting"
46 }});
47 function FormatTokenTypeOverrideTest(name) { test.mode(name, modeFormattingOverride, Array.prototype.slice.call(arguments, 1)); }
48 var modeET = CodeMirror.getMode(config, {name: "markdown", emoji: true});
49 function ET(name) { test.mode(name, modeET, Array.prototype.slice.call(arguments, 1)); }
50
51
52 FT("formatting_emAsterisk",
53 "[em&formatting&formatting-em *][em foo][em&formatting&formatting-em *]");
54
55 FT("formatting_emUnderscore",
56 "[em&formatting&formatting-em _][em foo][em&formatting&formatting-em _]");
57
58 FT("formatting_strongAsterisk",
59 "[strong&formatting&formatting-strong **][strong foo][strong&formatting&formatting-strong **]");
60
61 FT("formatting_strongUnderscore",
62 "[strong&formatting&formatting-strong __][strong foo][strong&formatting&formatting-strong __]");
63
64 FT("formatting_codeBackticks",
65 "[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]");
66
67 FT("formatting_doubleBackticks",
68 "[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]");
69
70 FT("formatting_atxHeader",
71 "[header&header-1&formatting&formatting-header&formatting-header-1 # ][header&header-1 foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]");
72
73 FT("formatting_setextHeader",
74 "[header&header-1 foo]",
75 "[header&header-1&formatting&formatting-header&formatting-header-1 =]");
76
77 FT("formatting_blockquote",
78 "[quote"e-1&formatting&formatting-quote&formatting-quote-1 > ][quote"e-1 foo]");
79
80 FT("formatting_list",
81 "[variable-2&formatting&formatting-list&formatting-list-ul - ][variable-2 foo]");
82 FT("formatting_list",
83 "[variable-2&formatting&formatting-list&formatting-list-ol 1. ][variable-2 foo]");
84
85 FT("formatting_link",
86 "[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 )]");
87
88 FT("formatting_linkReference",
89 "[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 ]]]",
90 "[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string&url http://example.com/]");
91
92 FT("formatting_linkWeb",
93 "[link&formatting&formatting-link <][link http://example.com/][link&formatting&formatting-link >]");
94
95 FT("formatting_linkEmail",
96 "[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]");
97
98 FT("formatting_escape",
99 "[formatting-escape \\*]");
100
101 FT("formatting_image",
102 "[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 )]");
103
104 FT("codeBlock",
105 "[comment&formatting&formatting-code-block ```css]",
106 "[tag foo]",
107 "[comment&formatting&formatting-code-block ```]");
108
109 MT("plainText",
110 "foo");
111
112 // Don't style single trailing space
113 MT("trailingSpace1",
114 "foo ");
115
116 // Two or more trailing spaces should be styled with line break character
117 MT("trailingSpace2",
118 "foo[trailing-space-a ][trailing-space-new-line ]");
119
120 MT("trailingSpace3",
121 "foo[trailing-space-a ][trailing-space-b ][trailing-space-new-line ]");
122
123 MT("trailingSpace4",
124 "foo[trailing-space-a ][trailing-space-b ][trailing-space-a ][trailing-space-new-line ]");
125
126 // Code blocks using 4 spaces (regardless of CodeMirror.tabSize value)
127 MT("codeBlocksUsing4Spaces",
128 " [comment foo]");
129
130 // Code blocks using 4 spaces with internal indentation
131 MT("codeBlocksUsing4SpacesIndentation",
132 " [comment bar]",
133 " [comment hello]",
134 " [comment world]",
135 " [comment foo]",
136 "bar");
137
138 // Code blocks should end even after extra indented lines
139 MT("codeBlocksWithTrailingIndentedLine",
140 " [comment foo]",
141 " [comment bar]",
142 " [comment baz]",
143 " ",
144 "hello");
145
146 // Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value)
147 MT("codeBlocksUsing1Tab",
148 "\t[comment foo]");
149
150 // No code blocks directly after paragraph
151 // http://spec.commonmark.org/0.19/#example-65
152 MT("noCodeBlocksAfterParagraph",
153 "Foo",
154 " Bar");
155
156 MT("codeBlocksAfterATX",
157 "[header&header-1 # foo]",
158 " [comment code]");
159
160 MT("codeBlocksAfterSetext",
161 "[header&header-2 foo]",
162 "[header&header-2 ---]",
163 " [comment code]");
164
165 MT("codeBlocksAfterFencedCode",
166 "[comment ```]",
167 "[comment foo]",
168 "[comment ```]",
169 " [comment code]");
170
171 // Inline code using backticks
172 MT("inlineCodeUsingBackticks",
173 "foo [comment `bar`]");
174
175 // Block code using single backtick (shouldn't work)
176 MT("blockCodeSingleBacktick",
177 "[comment `]",
178 "[comment foo]",
179 "[comment `]");
180
181 // Unclosed backticks
182 // Instead of simply marking as CODE, it would be nice to have an
183 // incomplete flag for CODE, that is styled slightly different.
184 MT("unclosedBackticks",
185 "foo [comment `bar]");
186
187 // Per documentation: "To include a literal backtick character within a
188 // code span, you can use multiple backticks as the opening and closing
189 // delimiters"
190 MT("doubleBackticks",
191 "[comment ``foo ` bar``]");
192
193 // Tests based on Dingus
194 // http://daringfireball.net/projects/markdown/dingus
195 //
196 // Multiple backticks within an inline code block
197 MT("consecutiveBackticks",
198 "[comment `foo```bar`]");
199
200 // Multiple backticks within an inline code block with a second code block
201 MT("consecutiveBackticks",
202 "[comment `foo```bar`] hello [comment `world`]");
203
204 // Unclosed with several different groups of backticks
205 MT("unclosedBackticks",
206 "[comment ``foo ``` bar` hello]");
207
208 // Closed with several different groups of backticks
209 MT("closedBackticks",
210 "[comment ``foo ``` bar` hello``] world");
211
212 // info string cannot contain backtick, thus should result in inline code
213 MT("closingFencedMarksOnSameLine",
214 "[comment ``` code ```] foo");
215
216 // atx headers
217 // http://daringfireball.net/projects/markdown/syntax#header
218
219 MT("atxH1",
220 "[header&header-1 # foo]");
221
222 MT("atxH2",
223 "[header&header-2 ## foo]");
224
225 MT("atxH3",
226 "[header&header-3 ### foo]");
227
228 MT("atxH4",
229 "[header&header-4 #### foo]");
230
231 MT("atxH5",
232 "[header&header-5 ##### foo]");
233
234 MT("atxH6",
235 "[header&header-6 ###### foo]");
236
237 // http://spec.commonmark.org/0.19/#example-24
238 MT("noAtxH7",
239 "####### foo");
240
241 // http://spec.commonmark.org/0.19/#example-25
242 MT("noAtxH1WithoutSpace",
243 "#5 bolt");
244
245 // CommonMark requires a space after # but most parsers don't
246 AtxNoSpaceTest("atxNoSpaceAllowed_H1NoSpace",
247 "[header&header-1 #foo]");
248
249 AtxNoSpaceTest("atxNoSpaceAllowed_H4NoSpace",
250 "[header&header-4 ####foo]");
251
252 AtxNoSpaceTest("atxNoSpaceAllowed_H1Space",
253 "[header&header-1 # foo]");
254
255 // Inline styles should be parsed inside headers
256 MT("atxH1inline",
257 "[header&header-1 # foo ][header&header-1&em *bar*]");
258
259 MT("atxIndentedTooMuch",
260 "[header&header-1 # foo]",
261 " [comment # bar]");
262
263 // disable atx inside blockquote until we implement proper blockquote inner mode
264 // TODO: fix to be CommonMark-compliant
265 MT("atxNestedInsideBlockquote",
266 "[quote"e-1 > # foo]");
267
268 MT("atxAfterBlockquote",
269 "[quote"e-1 > foo]",
270 "[header&header-1 # bar]");
271
272 // Setext headers - H1, H2
273 // Per documentation, "Any number of underlining =’s or -’s will work."
274 // http://daringfireball.net/projects/markdown/syntax#header
275 // Ideally, the text would be marked as `header` as well, but this is
276 // not really feasible at the moment. So, instead, we're testing against
277 // what works today, to avoid any regressions.
278 //
279 // Check if single underlining = works
280 MT("setextH1",
281 "[header&header-1 foo]",
282 "[header&header-1 =]");
283
284 // Check if 3+ ='s work
285 MT("setextH1",
286 "[header&header-1 foo]",
287 "[header&header-1 ===]");
288
289 // Check if single underlining - should not be interpreted
290 // as it might lead to an empty list:
291 // https://spec.commonmark.org/0.28/#setext-heading-underline
292 MT("setextH2Single",
293 "foo",
294 "-");
295
296 // Check if 3+ -'s work
297 MT("setextH2",
298 "[header&header-2 foo]",
299 "[header&header-2 ---]");
300
301 // http://spec.commonmark.org/0.19/#example-45
302 MT("setextH2AllowSpaces",
303 "[header&header-2 foo]",
304 " [header&header-2 ---- ]");
305
306 // http://spec.commonmark.org/0.19/#example-44
307 MT("noSetextAfterIndentedCodeBlock",
308 " [comment foo]",
309 "[hr ---]");
310
311 MT("setextAfterFencedCode",
312 "[comment ```]",
313 "[comment foo]",
314 "[comment ```]",
315 "[header&header-2 bar]",
316 "[header&header-2 ---]");
317
318 MT("setextAferATX",
319 "[header&header-1 # foo]",
320 "[header&header-2 bar]",
321 "[header&header-2 ---]");
322
323 // http://spec.commonmark.org/0.19/#example-51
324 MT("noSetextAfterQuote",
325 "[quote"e-1 > foo]",
326 "[hr ---]",
327 "",
328 "[quote"e-1 > foo]",
329 "[quote"e-1 bar]",
330 "[hr ---]");
331
332 MT("noSetextAfterList",
333 "[variable-2 - foo]",
334 "[hr ---]");
335
336 MT("noSetextAfterList_listContinuation",
337 "[variable-2 - foo]",
338 "bar",
339 "[hr ---]");
340
341 MT("setextAfterList_afterIndentedCode",
342 "[variable-2 - foo]",
343 "",
344 " [comment bar]",
345 "[header&header-2 baz]",
346 "[header&header-2 ---]");
347
348 MT("setextAfterList_afterFencedCodeBlocks",
349 "[variable-2 - foo]",
350 "",
351 " [comment ```]",
352 " [comment bar]",
353 " [comment ```]",
354 "[header&header-2 baz]",
355 "[header&header-2 ---]");
356
357 MT("setextAfterList_afterHeader",
358 "[variable-2 - foo]",
359 " [variable-2&header&header-1 # bar]",
360 "[header&header-2 baz]",
361 "[header&header-2 ---]");
362
363 MT("setextAfterList_afterHr",
364 "[variable-2 - foo]",
365 "",
366 " [hr ---]",
367 "[header&header-2 bar]",
368 "[header&header-2 ---]");
369
370 MT("setext_nestedInlineMarkup",
371 "[header&header-1 foo ][em&header&header-1 *bar*]",
372 "[header&header-1 =]");
373
374 MT("setext_linkDef",
375 "[link [[aaa]]:] [string&url http://google.com 'title']",
376 "[hr ---]");
377
378 // currently, looks max one line ahead, thus won't catch valid CommonMark
379 // markup
380 MT("setext_oneLineLookahead",
381 "foo",
382 "[header&header-1 bar]",
383 "[header&header-1 =]");
384
385 // ensure we regard space after a single dash as a list
386 MT("setext_emptyList",
387 "foo",
388 "[variable-2 - ]",
389 "foo");
390
391 // Single-line blockquote with trailing space
392 MT("blockquoteSpace",
393 "[quote"e-1 > foo]");
394
395 // Single-line blockquote
396 MT("blockquoteNoSpace",
397 "[quote"e-1 >foo]");
398
399 // No blank line before blockquote
400 MT("blockquoteNoBlankLine",
401 "foo",
402 "[quote"e-1 > bar]");
403
404 MT("blockquoteNested",
405 "[quote"e-1 > foo]",
406 "[quote"e-1 >][quote"e-2 > foo]",
407 "[quote"e-1 >][quote"e-2 >][quote"e-3 > foo]");
408
409 // ensure quote-level is inferred correctly even if indented
410 MT("blockquoteNestedIndented",
411 " [quote"e-1 > foo]",
412 " [quote"e-1 >][quote"e-2 > foo]",
413 " [quote"e-1 >][quote"e-2 >][quote"e-3 > foo]");
414
415 // ensure quote-level is inferred correctly even if indented
416 MT("blockquoteIndentedTooMuch",
417 "foo",
418 " > bar");
419
420 // Single-line blockquote followed by normal paragraph
421 MT("blockquoteThenParagraph",
422 "[quote"e-1 >foo]",
423 "",
424 "bar");
425
426 // Multi-line blockquote (lazy mode)
427 MT("multiBlockquoteLazy",
428 "[quote"e-1 >foo]",
429 "[quote"e-1 bar]");
430
431 // Multi-line blockquote followed by normal paragraph (lazy mode)
432 MT("multiBlockquoteLazyThenParagraph",
433 "[quote"e-1 >foo]",
434 "[quote"e-1 bar]",
435 "",
436 "hello");
437
438 // Multi-line blockquote (non-lazy mode)
439 MT("multiBlockquote",
440 "[quote"e-1 >foo]",
441 "[quote"e-1 >bar]");
442
443 // Multi-line blockquote followed by normal paragraph (non-lazy mode)
444 MT("multiBlockquoteThenParagraph",
445 "[quote"e-1 >foo]",
446 "[quote"e-1 >bar]",
447 "",
448 "hello");
449
450 // disallow lists inside blockquote for now because it causes problems outside blockquote
451 // TODO: fix to be CommonMark-compliant
452 MT("listNestedInBlockquote",
453 "[quote"e-1 > - foo]");
454
455 // disallow fenced blocks inside blockquote because it causes problems outside blockquote
456 // TODO: fix to be CommonMark-compliant
457 MT("fencedBlockNestedInBlockquote",
458 "[quote"e-1 > ```]",
459 "[quote"e-1 > code]",
460 "[quote"e-1 > ```]",
461 // ensure we still allow inline code
462 "[quote"e-1 > ][quote"e-1&comment `code`]");
463
464 // Header with leading space after continued blockquote (#3287, negative indentation)
465 MT("headerAfterContinuedBlockquote",
466 "[quote"e-1 > foo]",
467 "[quote"e-1 bar]",
468 "",
469 " [header&header-1 # hello]");
470
471 // Check list types
472
473 MT("listAsterisk",
474 "foo",
475 "bar",
476 "",
477 "[variable-2 * foo]",
478 "[variable-2 * bar]");
479
480 MT("listPlus",
481 "foo",
482 "bar",
483 "",
484 "[variable-2 + foo]",
485 "[variable-2 + bar]");
486
487 MT("listDash",
488 "foo",
489 "bar",
490 "",
491 "[variable-2 - foo]",
492 "[variable-2 - bar]");
493
494 MT("listNumber",
495 "foo",
496 "bar",
497 "",
498 "[variable-2 1. foo]",
499 "[variable-2 2. bar]");
500
501 MT("listFromParagraph",
502 "foo",
503 "[variable-2 1. bar]",
504 "[variable-2 2. hello]");
505
506 // List after hr
507 MT("listAfterHr",
508 "[hr ---]",
509 "[variable-2 - bar]");
510
511 // List after header
512 MT("listAfterHeader",
513 "[header&header-1 # foo]",
514 "[variable-2 - bar]");
515
516 // hr after list
517 MT("hrAfterList",
518 "[variable-2 - foo]",
519 "[hr -----]");
520
521 MT("hrAfterFencedCode",
522 "[comment ```]",
523 "[comment code]",
524 "[comment ```]",
525 "[hr ---]");
526
527 // allow hr inside lists
528 // (require prev line to be empty or hr, TODO: non-CommonMark-compliant)
529 MT("hrInsideList",
530 "[variable-2 - foo]",
531 "",
532 " [hr ---]",
533 " [hr ---]",
534 "",
535 " [comment ---]");
536
537 MT("consecutiveHr",
538 "[hr ---]",
539 "[hr ---]",
540 "[hr ---]");
541
542 // Formatting in lists (*)
543 MT("listAsteriskFormatting",
544 "[variable-2 * ][variable-2&em *foo*][variable-2 bar]",
545 "[variable-2 * ][variable-2&strong **foo**][variable-2 bar]",
546 "[variable-2 * ][variable-2&em&strong ***foo***][variable-2 bar]",
547 "[variable-2 * ][variable-2&comment `foo`][variable-2 bar]");
548
549 // Formatting in lists (+)
550 MT("listPlusFormatting",
551 "[variable-2 + ][variable-2&em *foo*][variable-2 bar]",
552 "[variable-2 + ][variable-2&strong **foo**][variable-2 bar]",
553 "[variable-2 + ][variable-2&em&strong ***foo***][variable-2 bar]",
554 "[variable-2 + ][variable-2&comment `foo`][variable-2 bar]");
555
556 // Formatting in lists (-)
557 MT("listDashFormatting",
558 "[variable-2 - ][variable-2&em *foo*][variable-2 bar]",
559 "[variable-2 - ][variable-2&strong **foo**][variable-2 bar]",
560 "[variable-2 - ][variable-2&em&strong ***foo***][variable-2 bar]",
561 "[variable-2 - ][variable-2&comment `foo`][variable-2 bar]");
562
563 // Formatting in lists (1.)
564 MT("listNumberFormatting",
565 "[variable-2 1. ][variable-2&em *foo*][variable-2 bar]",
566 "[variable-2 2. ][variable-2&strong **foo**][variable-2 bar]",
567 "[variable-2 3. ][variable-2&em&strong ***foo***][variable-2 bar]",
568 "[variable-2 4. ][variable-2&comment `foo`][variable-2 bar]");
569
570 // Paragraph lists
571 MT("listParagraph",
572 "[variable-2 * foo]",
573 "",
574 "[variable-2 * bar]");
575
576 // Multi-paragraph lists
577 //
578 // 4 spaces
579 MT("listMultiParagraph",
580 "[variable-2 * foo]",
581 "",
582 "[variable-2 * bar]",
583 "",
584 " [variable-2 hello]");
585
586 // 4 spaces, extra blank lines (should still be list, per Dingus)
587 MT("listMultiParagraphExtra",
588 "[variable-2 * foo]",
589 "",
590 "[variable-2 * bar]",
591 "",
592 "",
593 " [variable-2 hello]");
594
595 // 4 spaces, plus 1 space (should still be list, per Dingus)
596 MT("listMultiParagraphExtraSpace",
597 "[variable-2 * foo]",
598 "",
599 "[variable-2 * bar]",
600 "",
601 " [variable-2 hello]",
602 "",
603 " [variable-2 world]");
604
605 // 1 tab
606 MT("listTab",
607 "[variable-2 * foo]",
608 "",
609 "[variable-2 * bar]",
610 "",
611 "\t[variable-2 hello]");
612
613 // No indent
614 MT("listNoIndent",
615 "[variable-2 * foo]",
616 "",
617 "[variable-2 * bar]",
618 "",
619 "hello");
620
621 MT("listCommonMarkIndentationCode",
622 "[variable-2 * Code blocks also affect]",
623 " [variable-3 * The next level starts where the contents start.]",
624 " [variable-3 * Anything less than that will keep the item on the same level.]",
625 " [variable-3 * Each list item can indent the first level further and further.]",
626 " [variable-3 * For the most part, this makes sense while writing a list.]",
627 " [keyword * This means two items with same indentation can be different levels.]",
628 " [keyword * Each level has an indent requirement that can change between items.]",
629 " [keyword * A list item that meets this will be part of the next level.]",
630 " [variable-3 * Otherwise, it will be part of the level where it does meet this.]",
631 " [variable-2 * World]");
632
633 // should handle nested and un-nested lists
634 MT("listCommonMark_MixedIndents",
635 "[variable-2 * list1]",
636 " [variable-2 list1]",
637 " [variable-2&header&header-1 # heading still part of list1]",
638 " [variable-2 text after heading still part of list1]",
639 "",
640 " [comment indented codeblock]",
641 " [variable-2 list1 after code block]",
642 " [variable-3 * list2]",
643 // amount of spaces on empty lines between lists doesn't matter
644 " ",
645 // extra empty lines irrelevant
646 "",
647 "",
648 " [variable-3 indented text part of list2]",
649 " [keyword * list3]",
650 "",
651 " [variable-3 text at level of list2]",
652 "",
653 " [variable-2 de-indented text part of list1 again]",
654 "",
655 " [variable-2&comment ```]",
656 " [comment code]",
657 " [variable-2&comment ```]",
658 "",
659 " [variable-2 text after fenced code]");
660
661 // should correctly parse numbered list content indentation
662 MT("listCommonMark_NumeberedListIndent",
663 "[variable-2 1000. list with base indent of 6]",
664 "",
665 " [variable-2 text must be indented 6 spaces at minimum]",
666 "",
667 " [variable-2 9-spaces indented text still part of list]",
668 "",
669 " [comment indented codeblock starts at 10 spaces]",
670 "",
671 " [comment text indented by 5 spaces no longer belong to list]");
672
673 // should consider tab as 4 spaces
674 MT("listCommonMark_TabIndented",
675 "[variable-2 * list]",
676 "\t[variable-3 * list2]",
677 "",
678 "\t\t[variable-3 part of list2]");
679
680 MT("listAfterBlockquote",
681 "[quote"e-1 > foo]",
682 "[variable-2 - bar]");
683
684 // shouldn't create sublist if it's indented more than allowed
685 MT("nestedListIndentedTooMuch",
686 "[variable-2 - foo]",
687 " [variable-2 - bar]");
688
689 MT("listIndentedTooMuchAfterParagraph",
690 "foo",
691 " - bar");
692
693 // Blockquote
694 MT("blockquote",
695 "[variable-2 * foo]",
696 "",
697 "[variable-2 * bar]",
698 "",
699 " [variable-2"e"e-1 > hello]");
700
701 // Code block
702 MT("blockquoteCode",
703 "[variable-2 * foo]",
704 "",
705 "[variable-2 * bar]",
706 "",
707 " [comment > hello]",
708 "",
709 " [variable-2 world]");
710
711 // Code block followed by text
712 MT("blockquoteCodeText",
713 "[variable-2 * foo]",
714 "",
715 " [variable-2 bar]",
716 "",
717 " [comment hello]",
718 "",
719 " [variable-2 world]");
720
721 // Nested list
722
723 MT("listAsteriskNested",
724 "[variable-2 * foo]",
725 "",
726 " [variable-3 * bar]");
727
728 MT("listPlusNested",
729 "[variable-2 + foo]",
730 "",
731 " [variable-3 + bar]");
732
733 MT("listDashNested",
734 "[variable-2 - foo]",
735 "",
736 " [variable-3 - bar]");
737
738 MT("listNumberNested",
739 "[variable-2 1. foo]",
740 "",
741 " [variable-3 2. bar]");
742
743 MT("listMixed",
744 "[variable-2 * foo]",
745 "",
746 " [variable-3 + bar]",
747 "",
748 " [keyword - hello]",
749 "",
750 " [variable-2 1. world]");
751
752 MT("listBlockquote",
753 "[variable-2 * foo]",
754 "",
755 " [variable-3 + bar]",
756 "",
757 " [quote"e-1&variable-3 > hello]");
758
759 MT("listCode",
760 "[variable-2 * foo]",
761 "",
762 " [variable-3 + bar]",
763 "",
764 " [comment hello]");
765
766 // Code with internal indentation
767 MT("listCodeIndentation",
768 "[variable-2 * foo]",
769 "",
770 " [comment bar]",
771 " [comment hello]",
772 " [comment world]",
773 " [comment foo]",
774 " [variable-2 bar]");
775
776 // List nesting edge cases
777 MT("listNested",
778 "[variable-2 * foo]",
779 "",
780 " [variable-3 * bar]",
781 "",
782 " [variable-3 hello]"
783 );
784 MT("listNested",
785 "[variable-2 * foo]",
786 "",
787 " [variable-3 * bar]",
788 "",
789 " [keyword * foo]"
790 );
791
792 // Code followed by text
793 MT("listCodeText",
794 "[variable-2 * foo]",
795 "",
796 " [comment bar]",
797 "",
798 "hello");
799
800 // Following tests directly from official Markdown documentation
801 // http://daringfireball.net/projects/markdown/syntax#hr
802
803 MT("hrSpace",
804 "[hr * * *]");
805
806 MT("hr",
807 "[hr ***]");
808
809 MT("hrLong",
810 "[hr *****]");
811
812 MT("hrSpaceDash",
813 "[hr - - -]");
814
815 MT("hrDashLong",
816 "[hr ---------------------------------------]");
817
818 //Images
819 MT("Images",
820 "[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)]")
821
822 //Images with highlight alt text
823 MT("imageEm",
824 "[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)]");
825
826 MT("imageStrong",
827 "[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)]");
828
829 MT("imageEmStrong",
830 "[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)]");
831
832 // Inline link with title
833 MT("linkTitle",
834 "[link [[foo]]][string&url (http://example.com/ \"bar\")] hello");
835
836 // Inline link without title
837 MT("linkNoTitle",
838 "[link [[foo]]][string&url (http://example.com/)] bar");
839
840 // Inline link with image
841 MT("linkImage",
842 "[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");
843
844 // Inline link with Em
845 MT("linkEm",
846 "[link [[][link&em *foo*][link ]]][string&url (http://example.com/)] bar");
847
848 // Inline link with Strong
849 MT("linkStrong",
850 "[link [[][link&strong **foo**][link ]]][string&url (http://example.com/)] bar");
851
852 // Inline link with EmStrong
853 MT("linkEmStrong",
854 "[link [[][link&em&strong ***foo***][link ]]][string&url (http://example.com/)] bar");
855
856 MT("multilineLink",
857 "[link [[foo]",
858 "[link bar]]][string&url (https://foo#_a)]",
859 "should not be italics")
860
861 // Image with title
862 MT("imageTitle",
863 "[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/ \"bar\")] hello");
864
865 // Image without title
866 MT("imageNoTitle",
867 "[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/)] bar");
868
869 // Image with asterisks
870 MT("imageAsterisks",
871 "[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");
872
873 // Not a link. Should be normal text due to square brackets being used
874 // regularly in text, especially in quoted material, and no space is allowed
875 // between square brackets and parentheses (per Dingus).
876 MT("notALink",
877 "[link [[foo]]] (bar)");
878
879 // Reference-style links
880 MT("linkReference",
881 "[link [[foo]]][string&url [[bar]]] hello");
882
883 // Reference-style links with Em
884 MT("linkReferenceEm",
885 "[link [[][link&em *foo*][link ]]][string&url [[bar]]] hello");
886
887 // Reference-style links with Strong
888 MT("linkReferenceStrong",
889 "[link [[][link&strong **foo**][link ]]][string&url [[bar]]] hello");
890
891 // Reference-style links with EmStrong
892 MT("linkReferenceEmStrong",
893 "[link [[][link&em&strong ***foo***][link ]]][string&url [[bar]]] hello");
894
895 // Reference-style links with optional space separator (per documentation)
896 // "You can optionally use a space to separate the sets of brackets"
897 MT("linkReferenceSpace",
898 "[link [[foo]]] [string&url [[bar]]] hello");
899
900 // Should only allow a single space ("...use *a* space...")
901 MT("linkReferenceDoubleSpace",
902 "[link [[foo]]] [link [[bar]]] hello");
903
904 // Reference-style links with implicit link name
905 MT("linkImplicit",
906 "[link [[foo]]][string&url [[]]] hello");
907
908 // @todo It would be nice if, at some point, the document was actually
909 // checked to see if the referenced link exists
910
911 // Link label, for reference-style links (taken from documentation)
912
913 MT("labelNoTitle",
914 "[link [[foo]]:] [string&url http://example.com/]");
915
916 MT("labelIndented",
917 " [link [[foo]]:] [string&url http://example.com/]");
918
919 MT("labelSpaceTitle",
920 "[link [[foo bar]]:] [string&url http://example.com/ \"hello\"]");
921
922 MT("labelDoubleTitle",
923 "[link [[foo bar]]:] [string&url http://example.com/ \"hello\"] \"world\"");
924
925 MT("labelTitleDoubleQuotes",
926 "[link [[foo]]:] [string&url http://example.com/ \"bar\"]");
927
928 MT("labelTitleSingleQuotes",
929 "[link [[foo]]:] [string&url http://example.com/ 'bar']");
930
931 MT("labelTitleParentheses",
932 "[link [[foo]]:] [string&url http://example.com/ (bar)]");
933
934 MT("labelTitleInvalid",
935 "[link [[foo]]:] [string&url http://example.com/] bar");
936
937 MT("labelLinkAngleBrackets",
938 "[link [[foo]]:] [string&url <http://example.com/> \"bar\"]");
939
940 MT("labelTitleNextDoubleQuotes",
941 "[link [[foo]]:] [string&url http://example.com/]",
942 "[string \"bar\"] hello");
943
944 MT("labelTitleNextSingleQuotes",
945 "[link [[foo]]:] [string&url http://example.com/]",
946 "[string 'bar'] hello");
947
948 MT("labelTitleNextParentheses",
949 "[link [[foo]]:] [string&url http://example.com/]",
950 "[string (bar)] hello");
951
952 MT("labelTitleNextMixed",
953 "[link [[foo]]:] [string&url http://example.com/]",
954 "(bar\" hello");
955
956 MT("labelEscape",
957 "[link [[foo \\]] ]]:] [string&url http://example.com/]");
958
959 MT("labelEscapeColon",
960 "[link [[foo \\]]: bar]]:] [string&url http://example.com/]");
961
962 MT("labelEscapeEnd",
963 "\\[[foo\\]]: http://example.com/");
964
965 MT("linkWeb",
966 "[link <http://example.com/>] foo");
967
968 MT("linkWebDouble",
969 "[link <http://example.com/>] foo [link <http://example.com/>]");
970
971 MT("linkEmail",
972 "[link <user@example.com>] foo");
973
974 MT("linkEmailDouble",
975 "[link <user@example.com>] foo [link <user@example.com>]");
976
977 MT("emAsterisk",
978 "[em *foo*] bar");
979
980 MT("emUnderscore",
981 "[em _foo_] bar");
982
983 MT("emInWordAsterisk",
984 "foo[em *bar*]hello");
985
986 MT("emInWordUnderscore",
987 "foo_bar_hello");
988
989 // Per documentation: "...surround an * or _ with spaces, it’ll be
990 // treated as a literal asterisk or underscore."
991
992 MT("emEscapedBySpaceIn",
993 "foo [em _bar _ hello_] world");
994
995 MT("emEscapedBySpaceOut",
996 "foo _ bar [em _hello_] world");
997
998 MT("emEscapedByNewline",
999 "foo",
1000 "_ bar [em _hello_] world");
1001
1002 // Unclosed emphasis characters
1003 // Instead of simply marking as EM / STRONG, it would be nice to have an
1004 // incomplete flag for EM and STRONG, that is styled slightly different.
1005 MT("emIncompleteAsterisk",
1006 "foo [em *bar]");
1007
1008 MT("emIncompleteUnderscore",
1009 "foo [em _bar]");
1010
1011 MT("strongAsterisk",
1012 "[strong **foo**] bar");
1013
1014 MT("strongUnderscore",
1015 "[strong __foo__] bar");
1016
1017 MT("emStrongAsterisk",
1018 "[em *foo][em&strong **bar*][strong hello**] world");
1019
1020 MT("emStrongUnderscore",
1021 "[em _foo ][em&strong __bar_][strong hello__] world");
1022
1023 // "...same character must be used to open and close an emphasis span.""
1024 MT("emStrongMixed",
1025 "[em _foo][em&strong **bar*hello__ world]");
1026
1027 MT("emStrongMixed",
1028 "[em *foo ][em&strong __bar_hello** world]");
1029
1030 MT("linkWithNestedParens",
1031 "[link [[foo]]][string&url (bar(baz))]")
1032
1033 // These characters should be escaped:
1034 // \ backslash
1035 // ` backtick
1036 // * asterisk
1037 // _ underscore
1038 // {} curly braces
1039 // [] square brackets
1040 // () parentheses
1041 // # hash mark
1042 // + plus sign
1043 // - minus sign (hyphen)
1044 // . dot
1045 // ! exclamation mark
1046
1047 MT("escapeBacktick",
1048 "foo \\`bar\\`");
1049
1050 MT("doubleEscapeBacktick",
1051 "foo \\\\[comment `bar\\\\`]");
1052
1053 MT("escapeAsterisk",
1054 "foo \\*bar\\*");
1055
1056 MT("doubleEscapeAsterisk",
1057 "foo \\\\[em *bar\\\\*]");
1058
1059 MT("escapeUnderscore",
1060 "foo \\_bar\\_");
1061
1062 MT("doubleEscapeUnderscore",
1063 "foo \\\\[em _bar\\\\_]");
1064
1065 MT("escapeHash",
1066 "\\# foo");
1067
1068 MT("doubleEscapeHash",
1069 "\\\\# foo");
1070
1071 MT("escapeNewline",
1072 "\\",
1073 "[em *foo*]");
1074
1075 // Class override tests
1076 TokenTypeOverrideTest("overrideHeader1",
1077 "[override-header&override-header-1 # Foo]");
1078
1079 TokenTypeOverrideTest("overrideHeader2",
1080 "[override-header&override-header-2 ## Foo]");
1081
1082 TokenTypeOverrideTest("overrideHeader3",
1083 "[override-header&override-header-3 ### Foo]");
1084
1085 TokenTypeOverrideTest("overrideHeader4",
1086 "[override-header&override-header-4 #### Foo]");
1087
1088 TokenTypeOverrideTest("overrideHeader5",
1089 "[override-header&override-header-5 ##### Foo]");
1090
1091 TokenTypeOverrideTest("overrideHeader6",
1092 "[override-header&override-header-6 ###### Foo]");
1093
1094 TokenTypeOverrideTest("overrideCode",
1095 "[override-code `foo`]");
1096
1097 TokenTypeOverrideTest("overrideCodeBlock",
1098 "[override-code ```]",
1099 "[override-code foo]",
1100 "[override-code ```]");
1101
1102 TokenTypeOverrideTest("overrideQuote",
1103 "[override-quote&override-quote-1 > foo]",
1104 "[override-quote&override-quote-1 > bar]");
1105
1106 TokenTypeOverrideTest("overrideQuoteNested",
1107 "[override-quote&override-quote-1 > foo]",
1108 "[override-quote&override-quote-1 >][override-quote&override-quote-2 > bar]",
1109 "[override-quote&override-quote-1 >][override-quote&override-quote-2 >][override-quote&override-quote-3 > baz]");
1110
1111 TokenTypeOverrideTest("overrideLists",
1112 "[override-list1 - foo]",
1113 "",
1114 " [override-list2 + bar]",
1115 "",
1116 " [override-list3 * baz]",
1117 "",
1118 " [override-list1 1. qux]",
1119 "",
1120 " [override-list2 - quux]");
1121
1122 TokenTypeOverrideTest("overrideHr",
1123 "[override-hr * * *]");
1124
1125 TokenTypeOverrideTest("overrideImage",
1126 "[override-image&override-image-marker !][override-image&override-image-alt-text&link [[alt text]]][override-link-href&url (http://link.to/image.jpg)]");
1127
1128 TokenTypeOverrideTest("overrideLinkText",
1129 "[override-link-text [[foo]]][override-link-href&url (http://example.com)]");
1130
1131 TokenTypeOverrideTest("overrideLinkEmailAndInline",
1132 "[override-link-email <][override-link-inline foo@example.com>]");
1133
1134 TokenTypeOverrideTest("overrideEm",
1135 "[override-em *foo*]");
1136
1137 TokenTypeOverrideTest("overrideStrong",
1138 "[override-strong **foo**]");
1139
1140 TokenTypeOverrideTest("overrideStrikethrough",
1141 "[override-strikethrough ~~foo~~]");
1142
1143 TokenTypeOverrideTest("overrideEmoji",
1144 "[override-emoji :foo:]");
1145
1146 FormatTokenTypeOverrideTest("overrideFormatting",
1147 "[override-formatting-escape \\*]");
1148
1149 // Tests to make sure GFM-specific things aren't getting through
1150
1151 MT("taskList",
1152 "[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]");
1153
1154 MT("fencedCodeBlocks",
1155 "[comment ```]",
1156 "[comment foo]",
1157 "",
1158 "[comment bar]",
1159 "[comment ```]",
1160 "baz");
1161
1162 MT("fencedCodeBlocks_invalidClosingFence_trailingText",
1163 "[comment ```]",
1164 "[comment foo]",
1165 "[comment ``` must not have trailing text]",
1166 "[comment baz]");
1167
1168 MT("fencedCodeBlocks_invalidClosingFence_trailingTabs",
1169 "[comment ```]",
1170 "[comment foo]",
1171 "[comment ```\t]",
1172 "[comment baz]");
1173
1174 MT("fencedCodeBlocks_validClosingFence",
1175 "[comment ```]",
1176 "[comment foo]",
1177 // may have trailing spaces
1178 "[comment ``` ]",
1179 "baz");
1180
1181 MT("fencedCodeBlocksInList_closingFenceIndented",
1182 "[variable-2 - list]",
1183 " [variable-2&comment ```]",
1184 " [comment foo]",
1185 " [variable-2&comment ```]",
1186 " [variable-2 baz]");
1187
1188 MT("fencedCodeBlocksInList_closingFenceIndentedTooMuch",
1189 "[variable-2 - list]",
1190 " [variable-2&comment ```]",
1191 " [comment foo]",
1192 " [comment ```]",
1193 " [comment baz]");
1194
1195 MT("fencedCodeBlockModeSwitching",
1196 "[comment ```javascript]",
1197 "[variable foo]",
1198 "",
1199 "[comment ```]",
1200 "bar");
1201
1202 MT_noFencedHighlight("fencedCodeBlock_noHighlight",
1203 "[comment ```javascript]",
1204 "[comment foo]",
1205 "[comment ```]");
1206
1207 MT("fencedCodeBlockModeSwitchingObjc",
1208 "[comment ```objective-c]",
1209 "[keyword @property] [variable NSString] [operator *] [variable foo];",
1210 "[comment ```]",
1211 "bar");
1212
1213 MT("fencedCodeBlocksMultipleChars",
1214 "[comment `````]",
1215 "[comment foo]",
1216 "[comment ```]",
1217 "[comment foo]",
1218 "[comment `````]",
1219 "bar");
1220
1221 MT("fencedCodeBlocksTildes",
1222 "[comment ~~~]",
1223 "[comment foo]",
1224 "[comment ~~~]",
1225 "bar");
1226
1227 MT("fencedCodeBlocksTildesMultipleChars",
1228 "[comment ~~~~~]",
1229 "[comment ~~~]",
1230 "[comment foo]",
1231 "[comment ~~~~~]",
1232 "bar");
1233
1234 MT("fencedCodeBlocksMultipleChars",
1235 "[comment `````]",
1236 "[comment foo]",
1237 "[comment ```]",
1238 "[comment foo]",
1239 "[comment `````]",
1240 "bar");
1241
1242 MT("fencedCodeBlocksMixed",
1243 "[comment ~~~]",
1244 "[comment ```]",
1245 "[comment foo]",
1246 "[comment ~~~]",
1247 "bar");
1248
1249 MT("fencedCodeBlocksAfterBlockquote",
1250 "[quote"e-1 > foo]",
1251 "[comment ```]",
1252 "[comment bar]",
1253 "[comment ```]");
1254
1255 // fencedCode indented too much should act as simple indentedCode
1256 // (hence has no highlight formatting)
1257 FT("tooMuchIndentedFencedCode",
1258 " [comment ```]",
1259 " [comment code]",
1260 " [comment ```]");
1261
1262 MT("autoTerminateFencedCodeWhenLeavingList",
1263 "[variable-2 - list1]",
1264 " [variable-3 - list2]",
1265 " [variable-3&comment ```]",
1266 " [comment code]",
1267 " [variable-3 - list2]",
1268 " [variable-2&comment ```]",
1269 " [comment code]",
1270 "[quote"e-1 > foo]");
1271
1272 // Tests that require XML mode
1273
1274 MT("xmlMode",
1275 "[tag&bracket <][tag div][tag&bracket >]",
1276 " *foo*",
1277 " [tag&bracket <][tag http://github.com][tag&bracket />]",
1278 "[tag&bracket </][tag div][tag&bracket >]",
1279 "[link <http://github.com/>]");
1280
1281 MT("xmlModeWithMarkdownInside",
1282 "[tag&bracket <][tag div] [attribute markdown]=[string 1][tag&bracket >]",
1283 "[em *foo*]",
1284 "[link <http://github.com/>]",
1285 "[tag </div>]",
1286 "[link <http://github.com/>]",
1287 "[tag&bracket <][tag div][tag&bracket >]",
1288 "[tag&bracket </][tag div][tag&bracket >]");
1289
1290 MT("xmlModeLineBreakInTags",
1291 "[tag&bracket <][tag div] [attribute id]=[string \"1\"]",
1292 " [attribute class]=[string \"sth\"][tag&bracket >]xxx",
1293 "[tag&bracket </][tag div][tag&bracket >]");
1294
1295 MT("xmlModeCommentWithBlankLine",
1296 "[comment <!-- Hello]",
1297 "",
1298 "[comment World -->]");
1299
1300 MT("xmlModeCDATA",
1301 "[atom <![CDATA[ Hello]",
1302 "",
1303 "[atom FooBar]",
1304 "[atom Test ]]]]>]");
1305
1306 MT("xmlModePreprocessor",
1307 "[meta <?php] [meta echo '1234'; ?>]");
1308
1309 MT_noXml("xmlHighlightDisabled",
1310 "<div>foo</div>");
1311
1312 // Tests Emojis
1313
1314 ET("emojiDefault",
1315 "[builtin :foobar:]");
1316
1317 ET("emojiTable",
1318 " :--:");
1319})();