// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
;(function () {
var config = { tabSize: 4, indentUnit: 2 }
var mode = CodeMirror.getMode(config, 'markdown')
function MT(name) {
test.mode(name, mode, Array.prototype.slice.call(arguments, 1))
}
var modeHighlightFormatting = CodeMirror.getMode(config, { name: 'markdown', highlightFormatting: true })
function FT(name) {
test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1))
}
var modeMT_noXml = CodeMirror.getMode(config, { name: 'markdown', xml: false })
function MT_noXml(name) {
test.mode(name, modeMT_noXml, Array.prototype.slice.call(arguments, 1))
}
var modeMT_noFencedHighlight = CodeMirror.getMode(config, { name: 'markdown', fencedCodeBlockHighlighting: false })
function MT_noFencedHighlight(name) {
test.mode(name, modeMT_noFencedHighlight, Array.prototype.slice.call(arguments, 1))
}
var modeAtxNoSpace = CodeMirror.getMode(config, { name: 'markdown', allowAtxHeaderWithoutSpace: true })
function AtxNoSpaceTest(name) {
test.mode(name, modeAtxNoSpace, Array.prototype.slice.call(arguments, 1))
}
var modeOverrideClasses = CodeMirror.getMode(config, {
name: 'markdown',
strikethrough: true,
emoji: true,
tokenTypeOverrides: {
header: 'override-header',
code: 'override-code',
quote: 'override-quote',
list1: 'override-list1',
list2: 'override-list2',
list3: 'override-list3',
hr: 'override-hr',
image: 'override-image',
imageAltText: 'override-image-alt-text',
imageMarker: 'override-image-marker',
linkInline: 'override-link-inline',
linkEmail: 'override-link-email',
linkText: 'override-link-text',
linkHref: 'override-link-href',
em: 'override-em',
strong: 'override-strong',
strikethrough: 'override-strikethrough',
emoji: 'override-emoji',
},
})
function TokenTypeOverrideTest(name) {
test.mode(name, modeOverrideClasses, Array.prototype.slice.call(arguments, 1))
}
var modeFormattingOverride = CodeMirror.getMode(config, {
name: 'markdown',
highlightFormatting: true,
tokenTypeOverrides: {
formatting: 'override-formatting',
},
})
function FormatTokenTypeOverrideTest(name) {
test.mode(name, modeFormattingOverride, Array.prototype.slice.call(arguments, 1))
}
var modeET = CodeMirror.getMode(config, { name: 'markdown', emoji: true })
function ET(name) {
test.mode(name, modeET, Array.prototype.slice.call(arguments, 1))
}
FT('formatting_emAsterisk', '[em&formatting&formatting-em *][em foo][em&formatting&formatting-em *]')
FT('formatting_emUnderscore', '[em&formatting&formatting-em _][em foo][em&formatting&formatting-em _]')
FT('formatting_strongAsterisk', '[strong&formatting&formatting-strong **][strong foo][strong&formatting&formatting-strong **]')
FT('formatting_strongUnderscore', '[strong&formatting&formatting-strong __][strong foo][strong&formatting&formatting-strong __]')
FT('formatting_codeBackticks', '[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]')
FT('formatting_doubleBackticks', '[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]')
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 #]')
FT('formatting_setextHeader', '[header&header-1 foo]', '[header&header-1&formatting&formatting-header&formatting-header-1 =]')
FT('formatting_blockquote', '[quote"e-1&formatting&formatting-quote&formatting-quote-1 > ][quote"e-1 foo]')
FT('formatting_list', '[variable-2&formatting&formatting-list&formatting-list-ul - ][variable-2 foo]')
FT('formatting_list', '[variable-2&formatting&formatting-list&formatting-list-ol 1. ][variable-2 foo]')
FT(
'formatting_link',
'[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 )]'
)
FT(
'formatting_linkReference',
'[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 ]]]',
'[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string&url http://example.com/]'
)
FT('formatting_linkWeb', '[link&formatting&formatting-link <][link http://example.com/][link&formatting&formatting-link >]')
FT('formatting_linkEmail', '[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]')
FT('formatting_escape', '[formatting-escape \\*]')
FT(
'formatting_image',
'[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 )]'
)
FT('codeBlock', '[comment&formatting&formatting-code-block ```css]', '[tag foo]', '[comment&formatting&formatting-code-block ```]')
MT('plainText', 'foo')
// Don't style single trailing space
MT('trailingSpace1', 'foo ')
// Two or more trailing spaces should be styled with line break character
MT('trailingSpace2', 'foo[trailing-space-a ][trailing-space-new-line ]')
MT('trailingSpace3', 'foo[trailing-space-a ][trailing-space-b ][trailing-space-new-line ]')
MT('trailingSpace4', 'foo[trailing-space-a ][trailing-space-b ][trailing-space-a ][trailing-space-new-line ]')
// Code blocks using 4 spaces (regardless of CodeMirror.tabSize value)
MT('codeBlocksUsing4Spaces', ' [comment foo]')
// Code blocks using 4 spaces with internal indentation
MT('codeBlocksUsing4SpacesIndentation', ' [comment bar]', ' [comment hello]', ' [comment world]', ' [comment foo]', 'bar')
// Code blocks should end even after extra indented lines
MT('codeBlocksWithTrailingIndentedLine', ' [comment foo]', ' [comment bar]', ' [comment baz]', ' ', 'hello')
// Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value)
MT('codeBlocksUsing1Tab', '\t[comment foo]')
// No code blocks directly after paragraph
// http://spec.commonmark.org/0.19/#example-65
MT('noCodeBlocksAfterParagraph', 'Foo', ' Bar')
MT('codeBlocksAfterATX', '[header&header-1 # foo]', ' [comment code]')
MT('codeBlocksAfterSetext', '[header&header-2 foo]', '[header&header-2 ---]', ' [comment code]')
MT('codeBlocksAfterFencedCode', '[comment ```]', '[comment foo]', '[comment ```]', ' [comment code]')
// Inline code using backticks
MT('inlineCodeUsingBackticks', 'foo [comment `bar`]')
// Block code using single backtick (shouldn't work)
MT('blockCodeSingleBacktick', '[comment `]', '[comment foo]', '[comment `]')
// Unclosed backticks
// Instead of simply marking as CODE, it would be nice to have an
// incomplete flag for CODE, that is styled slightly different.
MT('unclosedBackticks', 'foo [comment `bar]')
// Per documentation: "To include a literal backtick character within a
// code span, you can use multiple backticks as the opening and closing
// delimiters"
MT('doubleBackticks', '[comment ``foo ` bar``]')
// Tests based on Dingus
// http://daringfireball.net/projects/markdown/dingus
//
// Multiple backticks within an inline code block
MT('consecutiveBackticks', '[comment `foo```bar`]')
// Multiple backticks within an inline code block with a second code block
MT('consecutiveBackticks', '[comment `foo```bar`] hello [comment `world`]')
// Unclosed with several different groups of backticks
MT('unclosedBackticks', '[comment ``foo ``` bar` hello]')
// Closed with several different groups of backticks
MT('closedBackticks', '[comment ``foo ``` bar` hello``] world')
// info string cannot contain backtick, thus should result in inline code
MT('closingFencedMarksOnSameLine', '[comment ``` code ```] foo')
// atx headers
// http://daringfireball.net/projects/markdown/syntax#header
MT('atxH1', '[header&header-1 # foo]')
MT('atxH2', '[header&header-2 ## foo]')
MT('atxH3', '[header&header-3 ### foo]')
MT('atxH4', '[header&header-4 #### foo]')
MT('atxH5', '[header&header-5 ##### foo]')
MT('atxH6', '[header&header-6 ###### foo]')
// http://spec.commonmark.org/0.19/#example-24
MT('noAtxH7', '####### foo')
// http://spec.commonmark.org/0.19/#example-25
MT('noAtxH1WithoutSpace', '#5 bolt')
// CommonMark requires a space after # but most parsers don't
AtxNoSpaceTest('atxNoSpaceAllowed_H1NoSpace', '[header&header-1 #foo]')
AtxNoSpaceTest('atxNoSpaceAllowed_H4NoSpace', '[header&header-4 ####foo]')
AtxNoSpaceTest('atxNoSpaceAllowed_H1Space', '[header&header-1 # foo]')
// Inline styles should be parsed inside headers
MT('atxH1inline', '[header&header-1 # foo ][header&header-1&em *bar*]')
MT('atxIndentedTooMuch', '[header&header-1 # foo]', ' [comment # bar]')
// disable atx inside blockquote until we implement proper blockquote inner mode
// TODO: fix to be CommonMark-compliant
MT('atxNestedInsideBlockquote', '[quote"e-1 > # foo]')
MT('atxAfterBlockquote', '[quote"e-1 > foo]', '[header&header-1 # bar]')
// Setext headers - H1, H2
// Per documentation, "Any number of underlining =’s or -’s will work."
// http://daringfireball.net/projects/markdown/syntax#header
// Ideally, the text would be marked as `header` as well, but this is
// not really feasible at the moment. So, instead, we're testing against
// what works today, to avoid any regressions.
//
// Check if single underlining = works
MT('setextH1', '[header&header-1 foo]', '[header&header-1 =]')
// Check if 3+ ='s work
MT('setextH1', '[header&header-1 foo]', '[header&header-1 ===]')
// Check if single underlining - should not be interpreted
// as it might lead to an empty list:
// https://spec.commonmark.org/0.28/#setext-heading-underline
MT('setextH2Single', 'foo', '-')
// Check if 3+ -'s work
MT('setextH2', '[header&header-2 foo]', '[header&header-2 ---]')
// http://spec.commonmark.org/0.19/#example-45
MT('setextH2AllowSpaces', '[header&header-2 foo]', ' [header&header-2 ---- ]')
// http://spec.commonmark.org/0.19/#example-44
MT('noSetextAfterIndentedCodeBlock', ' [comment foo]', '[hr ---]')
MT('setextAfterFencedCode', '[comment ```]', '[comment foo]', '[comment ```]', '[header&header-2 bar]', '[header&header-2 ---]')
MT('setextAfterATX', '[header&header-1 # foo]', '[header&header-2 bar]', '[header&header-2 ---]')
// http://spec.commonmark.org/0.19/#example-51
MT('noSetextAfterQuote', '[quote"e-1 > foo]', '[hr ---]', '', '[quote"e-1 > foo]', '[quote"e-1 bar]', '[hr ---]')
MT('noSetextAfterList', '[variable-2 - foo]', '[hr ---]')
MT('noSetextAfterList_listContinuation', '[variable-2 - foo]', 'bar', '[hr ---]')
MT('setextAfterList_afterIndentedCode', '[variable-2 - foo]', '', ' [comment bar]', '[header&header-2 baz]', '[header&header-2 ---]')
MT('setextAfterList_afterFencedCodeBlocks', '[variable-2 - foo]', '', ' [comment ```]', ' [comment bar]', ' [comment ```]', '[header&header-2 baz]', '[header&header-2 ---]')
MT('setextAfterList_afterHeader', '[variable-2 - foo]', ' [variable-2&header&header-1 # bar]', '[header&header-2 baz]', '[header&header-2 ---]')
MT('setextAfterList_afterHr', '[variable-2 - foo]', '', ' [hr ---]', '[header&header-2 bar]', '[header&header-2 ---]')
MT('setext_nestedInlineMarkup', '[header&header-1 foo ][em&header&header-1 *bar*]', '[header&header-1 =]')
MT('setext_linkDef', "[link [[aaa]]:] [string&url http://google.com 'title']", '[hr ---]')
// currently, looks max one line ahead, thus won't catch valid CommonMark
// markup
MT('setext_oneLineLookahead', 'foo', '[header&header-1 bar]', '[header&header-1 =]')
// ensure we regard space after a single dash as a list
MT('setext_emptyList', 'foo', '[variable-2 - ]', 'foo')
// Single-line blockquote with trailing space
MT('blockquoteSpace', '[quote"e-1 > foo]')
// Single-line blockquote
MT('blockquoteNoSpace', '[quote"e-1 >foo]')
// No blank line before blockquote
MT('blockquoteNoBlankLine', 'foo', '[quote"e-1 > bar]')
MT('blockquoteNested', '[quote"e-1 > foo]', '[quote"e-1 >][quote"e-2 > foo]', '[quote"e-1 >][quote"e-2 >][quote"e-3 > foo]')
// ensure quote-level is inferred correctly even if indented
MT('blockquoteNestedIndented', ' [quote"e-1 > foo]', ' [quote"e-1 >][quote"e-2 > foo]', ' [quote"e-1 >][quote"e-2 >][quote"e-3 > foo]')
// ensure quote-level is inferred correctly even if indented
MT('blockquoteIndentedTooMuch', 'foo', ' > bar')
// Single-line blockquote followed by normal paragraph
MT('blockquoteThenParagraph', '[quote"e-1 >foo]', '', 'bar')
// Multi-line blockquote (lazy mode)
MT('multiBlockquoteLazy', '[quote"e-1 >foo]', '[quote"e-1 bar]')
// Multi-line blockquote followed by normal paragraph (lazy mode)
MT('multiBlockquoteLazyThenParagraph', '[quote"e-1 >foo]', '[quote"e-1 bar]', '', 'hello')
// Multi-line blockquote (non-lazy mode)
MT('multiBlockquote', '[quote"e-1 >foo]', '[quote"e-1 >bar]')
// Multi-line blockquote followed by normal paragraph (non-lazy mode)
MT('multiBlockquoteThenParagraph', '[quote"e-1 >foo]', '[quote"e-1 >bar]', '', 'hello')
// disallow lists inside blockquote for now because it causes problems outside blockquote
// TODO: fix to be CommonMark-compliant
MT('listNestedInBlockquote', '[quote"e-1 > - foo]')
// disallow fenced blocks inside blockquote because it causes problems outside blockquote
// TODO: fix to be CommonMark-compliant
MT(
'fencedBlockNestedInBlockquote',
'[quote"e-1 > ```]',
'[quote"e-1 > code]',
'[quote"e-1 > ```]',
// ensure we still allow inline code
'[quote"e-1 > ][quote"e-1&comment `code`]'
)
// Header with leading space after continued blockquote (#3287, negative indentation)
MT('headerAfterContinuedBlockquote', '[quote"e-1 > foo]', '[quote"e-1 bar]', '', ' [header&header-1 # hello]')
// Check list types
MT('listAsterisk', 'foo', 'bar', '', '[variable-2 * foo]', '[variable-2 * bar]')
MT('listPlus', 'foo', 'bar', '', '[variable-2 + foo]', '[variable-2 + bar]')
MT('listDash', 'foo', 'bar', '', '[variable-2 - foo]', '[variable-2 - bar]')
MT('listNumber', 'foo', 'bar', '', '[variable-2 1. foo]', '[variable-2 2. bar]')
MT('listFromParagraph', 'foo', '[variable-2 1. bar]', '[variable-2 2. hello]')
// List after hr
MT('listAfterHr', '[hr ---]', '[variable-2 - bar]')
// List after header
MT('listAfterHeader', '[header&header-1 # foo]', '[variable-2 - bar]')
// hr after list
MT('hrAfterList', '[variable-2 - foo]', '[hr -----]')
MT('hrAfterFencedCode', '[comment ```]', '[comment code]', '[comment ```]', '[hr ---]')
// allow hr inside lists
// (require prev line to be empty or hr, TODO: non-CommonMark-compliant)
MT('hrInsideList', '[variable-2 - foo]', '', ' [hr ---]', ' [hr ---]', '', ' [comment ---]')
MT('consecutiveHr', '[hr ---]', '[hr ---]', '[hr ---]')
// Formatting in lists (*)
MT(
'listAsteriskFormatting',
'[variable-2 * ][variable-2&em *foo*][variable-2 bar]',
'[variable-2 * ][variable-2&strong **foo**][variable-2 bar]',
'[variable-2 * ][variable-2&em&strong ***foo***][variable-2 bar]',
'[variable-2 * ][variable-2&comment `foo`][variable-2 bar]'
)
// Formatting in lists (+)
MT(
'listPlusFormatting',
'[variable-2 + ][variable-2&em *foo*][variable-2 bar]',
'[variable-2 + ][variable-2&strong **foo**][variable-2 bar]',
'[variable-2 + ][variable-2&em&strong ***foo***][variable-2 bar]',
'[variable-2 + ][variable-2&comment `foo`][variable-2 bar]'
)
// Formatting in lists (-)
MT(
'listDashFormatting',
'[variable-2 - ][variable-2&em *foo*][variable-2 bar]',
'[variable-2 - ][variable-2&strong **foo**][variable-2 bar]',
'[variable-2 - ][variable-2&em&strong ***foo***][variable-2 bar]',
'[variable-2 - ][variable-2&comment `foo`][variable-2 bar]'
)
// Formatting in lists (1.)
MT(
'listNumberFormatting',
'[variable-2 1. ][variable-2&em *foo*][variable-2 bar]',
'[variable-2 2. ][variable-2&strong **foo**][variable-2 bar]',
'[variable-2 3. ][variable-2&em&strong ***foo***][variable-2 bar]',
'[variable-2 4. ][variable-2&comment `foo`][variable-2 bar]'
)
// Paragraph lists
MT('listParagraph', '[variable-2 * foo]', '', '[variable-2 * bar]')
// Multi-paragraph lists
//
// 4 spaces
MT('listMultiParagraph', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2 hello]')
// 4 spaces, extra blank lines (should still be list, per Dingus)
MT('listMultiParagraphExtra', '[variable-2 * foo]', '', '[variable-2 * bar]', '', '', ' [variable-2 hello]')
// 4 spaces, plus 1 space (should still be list, per Dingus)
MT('listMultiParagraphExtraSpace', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2 hello]', '', ' [variable-2 world]')
// 1 tab
MT('listTab', '[variable-2 * foo]', '', '[variable-2 * bar]', '', '\t[variable-2 hello]')
// No indent
MT('listNoIndent', '[variable-2 * foo]', '', '[variable-2 * bar]', '', 'hello')
MT(
'listCommonMarkIndentationCode',
'[variable-2 * Code blocks also affect]',
' [variable-3 * The next level starts where the contents start.]',
' [variable-3 * Anything less than that will keep the item on the same level.]',
' [variable-3 * Each list item can indent the first level further and further.]',
' [variable-3 * For the most part, this makes sense while writing a list.]',
' [keyword * This means two items with same indentation can be different levels.]',
' [keyword * Each level has an indent requirement that can change between items.]',
' [keyword * A list item that meets this will be part of the next level.]',
' [variable-3 * Otherwise, it will be part of the level where it does meet this.]',
' [variable-2 * World]'
)
// should handle nested and un-nested lists
MT(
'listCommonMark_MixedIndents',
'[variable-2 * list1]',
' [variable-2 list1]',
' [variable-2&header&header-1 # heading still part of list1]',
' [variable-2 text after heading still part of list1]',
'',
' [comment indented codeblock]',
' [variable-2 list1 after code block]',
' [variable-3 * list2]',
// amount of spaces on empty lines between lists doesn't matter
' ',
// extra empty lines irrelevant
'',
'',
' [variable-3 indented text part of list2]',
' [keyword * list3]',
'',
' [variable-3 text at level of list2]',
'',
' [variable-2 de-indented text part of list1 again]',
'',
' [variable-2&comment ```]',
' [comment code]',
' [variable-2&comment ```]',
'',
' [variable-2 text after fenced code]'
)
// should correctly parse numbered list content indentation
MT(
'listCommonMark_NumberedListIndent',
'[variable-2 1000. list with base indent of 6]',
'',
' [variable-2 text must be indented 6 spaces at minimum]',
'',
' [variable-2 9-spaces indented text still part of list]',
'',
' [comment indented codeblock starts at 10 spaces]',
'',
' [comment text indented by 5 spaces no longer belong to list]'
)
// should consider tab as 4 spaces
MT('listCommonMark_TabIndented', '[variable-2 * list]', '\t[variable-3 * list2]', '', '\t\t[variable-3 part of list2]')
MT('listAfterBlockquote', '[quote"e-1 > foo]', '[variable-2 - bar]')
// shouldn't create sublist if it's indented more than allowed
MT('nestedListIndentedTooMuch', '[variable-2 - foo]', ' [variable-2 - bar]')
MT('listIndentedTooMuchAfterParagraph', 'foo', ' - bar')
// Blockquote
MT('blockquote', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2"e"e-1 > hello]')
// Code block
MT('blockquoteCode', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [comment > hello]', '', ' [variable-2 world]')
// Code block followed by text
MT('blockquoteCodeText', '[variable-2 * foo]', '', ' [variable-2 bar]', '', ' [comment hello]', '', ' [variable-2 world]')
// Nested list
MT('listAsteriskNested', '[variable-2 * foo]', '', ' [variable-3 * bar]')
MT('listPlusNested', '[variable-2 + foo]', '', ' [variable-3 + bar]')
MT('listDashNested', '[variable-2 - foo]', '', ' [variable-3 - bar]')
MT('listNumberNested', '[variable-2 1. foo]', '', ' [variable-3 2. bar]')
MT('listMixed', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [keyword - hello]', '', ' [variable-2 1. world]')
MT('listBlockquote', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [quote"e-1&variable-3 > hello]')
MT('listCode', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [comment hello]')
// Code with internal indentation
MT('listCodeIndentation', '[variable-2 * foo]', '', ' [comment bar]', ' [comment hello]', ' [comment world]', ' [comment foo]', ' [variable-2 bar]')
// List nesting edge cases
MT('listNested', '[variable-2 * foo]', '', ' [variable-3 * bar]', '', ' [variable-3 hello]')
MT('listNested', '[variable-2 * foo]', '', ' [variable-3 * bar]', '', ' [keyword * foo]')
// Code followed by text
MT('listCodeText', '[variable-2 * foo]', '', ' [comment bar]', '', 'hello')
// Following tests directly from official Markdown documentation
// http://daringfireball.net/projects/markdown/syntax#hr
MT('hrSpace', '[hr * * *]')
MT('hr', '[hr ***]')
MT('hrLong', '[hr *****]')
MT('hrSpaceDash', '[hr - - -]')
MT('hrDashLong', '[hr ---------------------------------------]')
//Images
MT('Images', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)]')
//Images with highlight alt text
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)]')
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)]')
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)]')
// Inline link with title
MT('linkTitle', '[link [[foo]]][string&url (http://example.com/ "bar")] hello')
// Inline link without title
MT('linkNoTitle', '[link [[foo]]][string&url (http://example.com/)] bar')
// Inline link with image
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')
// Inline link with Em
MT('linkEm', '[link [[][link&em *foo*][link ]]][string&url (http://example.com/)] bar')
// Inline link with Strong
MT('linkStrong', '[link [[][link&strong **foo**][link ]]][string&url (http://example.com/)] bar')
// Inline link with EmStrong
MT('linkEmStrong', '[link [[][link&em&strong ***foo***][link ]]][string&url (http://example.com/)] bar')
MT('multilineLink', '[link [[foo]', '[link bar]]][string&url (https://foo#_a)]', 'should not be italics')
// Image with title
MT('imageTitle', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/ "bar")] hello')
// Image without title
MT('imageNoTitle', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/)] bar')
// Image with asterisks
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')
// Not a link. Should be normal text due to square brackets being used
// regularly in text, especially in quoted material, and no space is allowed
// between square brackets and parentheses (per Dingus).
MT('notALink', '[link [[foo]]] (bar)')
// Reference-style links
MT('linkReference', '[link [[foo]]][string&url [[bar]]] hello')
// Reference-style links with Em
MT('linkReferenceEm', '[link [[][link&em *foo*][link ]]][string&url [[bar]]] hello')
// Reference-style links with Strong
MT('linkReferenceStrong', '[link [[][link&strong **foo**][link ]]][string&url [[bar]]] hello')
// Reference-style links with EmStrong
MT('linkReferenceEmStrong', '[link [[][link&em&strong ***foo***][link ]]][string&url [[bar]]] hello')
// Reference-style links with optional space separator (per documentation)
// "You can optionally use a space to separate the sets of brackets"
MT('linkReferenceSpace', '[link [[foo]]] [string&url [[bar]]] hello')
// Should only allow a single space ("...use *a* space...")
MT('linkReferenceDoubleSpace', '[link [[foo]]] [link [[bar]]] hello')
// Reference-style links with implicit link name
MT('linkImplicit', '[link [[foo]]][string&url [[]]] hello')
// @todo It would be nice if, at some point, the document was actually
// checked to see if the referenced link exists
// Link label, for reference-style links (taken from documentation)
MT('labelNoTitle', '[link [[foo]]:] [string&url http://example.com/]')
MT('labelIndented', ' [link [[foo]]:] [string&url http://example.com/]')
MT('labelSpaceTitle', '[link [[foo bar]]:] [string&url http://example.com/ "hello"]')
MT('labelDoubleTitle', '[link [[foo bar]]:] [string&url http://example.com/ "hello"] "world"')
MT('labelTitleDoubleQuotes', '[link [[foo]]:] [string&url http://example.com/ "bar"]')
MT('labelTitleSingleQuotes', "[link [[foo]]:] [string&url http://example.com/ 'bar']")
MT('labelTitleParentheses', '[link [[foo]]:] [string&url http://example.com/ (bar)]')
MT('labelTitleInvalid', '[link [[foo]]:] [string&url http://example.com/] bar')
MT('labelLinkAngleBrackets', '[link [[foo]]:] [string&url "bar"]')
MT('labelTitleNextDoubleQuotes', '[link [[foo]]:] [string&url http://example.com/]', '[string "bar"] hello')
MT('labelTitleNextSingleQuotes', '[link [[foo]]:] [string&url http://example.com/]', "[string 'bar'] hello")
MT('labelTitleNextParentheses', '[link [[foo]]:] [string&url http://example.com/]', '[string (bar)] hello')
MT('labelTitleNextMixed', '[link [[foo]]:] [string&url http://example.com/]', '(bar" hello')
MT('labelEscape', '[link [[foo \\]] ]]:] [string&url http://example.com/]')
MT('labelEscapeColon', '[link [[foo \\]]: bar]]:] [string&url http://example.com/]')
MT('labelEscapeEnd', '\\[[foo\\]]: http://example.com/')
MT('linkWeb', '[link ] foo')
MT('linkWebDouble', '[link ] foo [link ]')
MT('linkEmail', '[link ] foo')
MT('linkEmailDouble', '[link ] foo [link ]')
MT('emAsterisk', '[em *foo*] bar')
MT('emUnderscore', '[em _foo_] bar')
MT('emInWordAsterisk', 'foo[em *bar*]hello')
MT('emInWordUnderscore', 'foo_bar_hello')
// Per documentation: "...surround an * or _ with spaces, it’ll be
// treated as a literal asterisk or underscore."
MT('emEscapedBySpaceIn', 'foo [em _bar _ hello_] world')
MT('emEscapedBySpaceOut', 'foo _ bar [em _hello_] world')
MT('emEscapedByNewline', 'foo', '_ bar [em _hello_] world')
// Unclosed emphasis characters
// Instead of simply marking as EM / STRONG, it would be nice to have an
// incomplete flag for EM and STRONG, that is styled slightly different.
MT('emIncompleteAsterisk', 'foo [em *bar]')
MT('emIncompleteUnderscore', 'foo [em _bar]')
MT('strongAsterisk', '[strong **foo**] bar')
MT('strongUnderscore', '[strong __foo__] bar')
MT('emStrongAsterisk', '[em *foo][em&strong **bar*][strong hello**] world')
MT('emStrongUnderscore', '[em _foo ][em&strong __bar_][strong hello__] world')
// "...same character must be used to open and close an emphasis span.""
MT('emStrongMixed', '[em _foo][em&strong **bar*hello__ world]')
MT('emStrongMixed', '[em *foo ][em&strong __bar_hello** world]')
MT('linkWithNestedParens', '[link [[foo]]][string&url (bar(baz))]')
// These characters should be escaped:
// \ backslash
// ` backtick
// * asterisk
// _ underscore
// {} curly braces
// [] square brackets
// () parentheses
// # hash mark
// + plus sign
// - minus sign (hyphen)
// . dot
// ! exclamation mark
MT('escapeBacktick', 'foo \\`bar\\`')
MT('doubleEscapeBacktick', 'foo \\\\[comment `bar\\\\`]')
MT('escapeAsterisk', 'foo \\*bar\\*')
MT('doubleEscapeAsterisk', 'foo \\\\[em *bar\\\\*]')
MT('escapeUnderscore', 'foo \\_bar\\_')
MT('doubleEscapeUnderscore', 'foo \\\\[em _bar\\\\_]')
MT('escapeHash', '\\# foo')
MT('doubleEscapeHash', '\\\\# foo')
MT('escapeNewline', '\\', '[em *foo*]')
// Class override tests
TokenTypeOverrideTest('overrideHeader1', '[override-header&override-header-1 # Foo]')
TokenTypeOverrideTest('overrideHeader2', '[override-header&override-header-2 ## Foo]')
TokenTypeOverrideTest('overrideHeader3', '[override-header&override-header-3 ### Foo]')
TokenTypeOverrideTest('overrideHeader4', '[override-header&override-header-4 #### Foo]')
TokenTypeOverrideTest('overrideHeader5', '[override-header&override-header-5 ##### Foo]')
TokenTypeOverrideTest('overrideHeader6', '[override-header&override-header-6 ###### Foo]')
TokenTypeOverrideTest('overrideCode', '[override-code `foo`]')
TokenTypeOverrideTest('overrideCodeBlock', '[override-code ```]', '[override-code foo]', '[override-code ```]')
TokenTypeOverrideTest('overrideQuote', '[override-quote&override-quote-1 > foo]', '[override-quote&override-quote-1 > bar]')
TokenTypeOverrideTest(
'overrideQuoteNested',
'[override-quote&override-quote-1 > foo]',
'[override-quote&override-quote-1 >][override-quote&override-quote-2 > bar]',
'[override-quote&override-quote-1 >][override-quote&override-quote-2 >][override-quote&override-quote-3 > baz]'
)
TokenTypeOverrideTest(
'overrideLists',
'[override-list1 - foo]',
'',
' [override-list2 + bar]',
'',
' [override-list3 * baz]',
'',
' [override-list1 1. qux]',
'',
' [override-list2 - quux]'
)
TokenTypeOverrideTest('overrideHr', '[override-hr * * *]')
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)]')
TokenTypeOverrideTest('overrideLinkText', '[override-link-text [[foo]]][override-link-href&url (http://example.com)]')
TokenTypeOverrideTest('overrideLinkEmailAndInline', '[override-link-email <][override-link-inline foo@example.com>]')
TokenTypeOverrideTest('overrideEm', '[override-em *foo*]')
TokenTypeOverrideTest('overrideStrong', '[override-strong **foo**]')
TokenTypeOverrideTest('overrideStrikethrough', '[override-strikethrough ~~foo~~]')
TokenTypeOverrideTest('overrideEmoji', '[override-emoji :foo:]')
FormatTokenTypeOverrideTest('overrideFormatting', '[override-formatting-escape \\*]')
// Tests to make sure GFM-specific things aren't getting through
MT('taskList', '[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]')
MT('fencedCodeBlocks', '[comment ```]', '[comment foo]', '', '[comment bar]', '[comment ```]', 'baz')
MT('fencedCodeBlocks_invalidClosingFence_trailingText', '[comment ```]', '[comment foo]', '[comment ``` must not have trailing text]', '[comment baz]')
MT('fencedCodeBlocks_invalidClosingFence_trailingTabs', '[comment ```]', '[comment foo]', '[comment ```\t]', '[comment baz]')
MT(
'fencedCodeBlocks_validClosingFence',
'[comment ```]',
'[comment foo]',
// may have trailing spaces
'[comment ``` ]',
'baz'
)
MT('fencedCodeBlocksInList_closingFenceIndented', '[variable-2 - list]', ' [variable-2&comment ```]', ' [comment foo]', ' [variable-2&comment ```]', ' [variable-2 baz]')
MT('fencedCodeBlocksInList_closingFenceIndentedTooMuch', '[variable-2 - list]', ' [variable-2&comment ```]', ' [comment foo]', ' [comment ```]', ' [comment baz]')
MT('fencedCodeBlockModeSwitching', '[comment ```javascript]', '[variable foo]', '', '[comment ```]', 'bar')
MT_noFencedHighlight('fencedCodeBlock_noHighlight', '[comment ```javascript]', '[comment foo]', '[comment ```]')
MT('fencedCodeBlockModeSwitchingObjc', '[comment ```objective-c]', '[keyword @property] [variable NSString] [operator *] [variable foo];', '[comment ```]', 'bar')
MT('fencedCodeBlocksMultipleChars', '[comment `````]', '[comment foo]', '[comment ```]', '[comment foo]', '[comment `````]', 'bar')
MT('fencedCodeBlocksTildes', '[comment ~~~]', '[comment foo]', '[comment ~~~]', 'bar')
MT('fencedCodeBlocksTildesMultipleChars', '[comment ~~~~~]', '[comment ~~~]', '[comment foo]', '[comment ~~~~~]', 'bar')
MT('fencedCodeBlocksMultipleChars', '[comment `````]', '[comment foo]', '[comment ```]', '[comment foo]', '[comment `````]', 'bar')
MT('fencedCodeBlocksMixed', '[comment ~~~]', '[comment ```]', '[comment foo]', '[comment ~~~]', 'bar')
MT('fencedCodeBlocksAfterBlockquote', '[quote"e-1 > foo]', '[comment ```]', '[comment bar]', '[comment ```]')
// fencedCode indented too much should act as simple indentedCode
// (hence has no highlight formatting)
FT('tooMuchIndentedFencedCode', ' [comment ```]', ' [comment code]', ' [comment ```]')
MT(
'autoTerminateFencedCodeWhenLeavingList',
'[variable-2 - list1]',
' [variable-3 - list2]',
' [variable-3&comment ```]',
' [comment code]',
' [variable-3 - list2]',
' [variable-2&comment ```]',
' [comment code]',
'[quote"e-1 > foo]'
)
// Tests that require XML mode
MT(
'xmlMode',
'[tag&bracket <][tag div][tag&bracket >]',
' *foo*',
' [tag&bracket <][tag http://github.com][tag&bracket />]',
'[tag&bracket ][tag div][tag&bracket >]',
'[link ]'
)
MT(
'xmlModeWithMarkdownInside',
'[tag&bracket <][tag div] [attribute markdown]=[string 1][tag&bracket >]',
'[em *foo*]',
'[link ]',
'[tag ]',
'[link ]',
'[tag&bracket <][tag div][tag&bracket >]',
'[tag&bracket ][tag div][tag&bracket >]'
)
MT('xmlModeLineBreakInTags', '[tag&bracket <][tag div] [attribute id]=[string "1"]', ' [attribute class]=[string "sth"][tag&bracket >]xxx', '[tag&bracket ][tag div][tag&bracket >]')
MT('xmlModeCommentWithBlankLine', '[comment ]')
MT('xmlModeCDATA', '[atom ]')
MT('xmlModePreprocessor', "[meta ]")
MT_noXml('xmlHighlightDisabled', 'foo
')
// Tests Emojis
ET('emojiDefault', '[builtin :foobar:]')
ET('emojiTable', ' :--:')
})()