Commit b9ec6fd8d1477ddea4a93e4e85c2af410f0d2ac3

Michael Schmidt 2019-07-13T15:52:36

Markdown: Improved URLs (#1955) This fixes the known issue that inline styles (bold, italic, strike) break URLs. Inline styles are now properly highlighted inside URLs.

diff --git a/components/prism-markdown.js b/components/prism-markdown.js
index 94adc83..00567f7 100644
--- a/components/prism-markdown.js
+++ b/components/prism-markdown.js
@@ -1,7 +1,7 @@
 (function (Prism) {
 
 	// Allow only one line break
-	var inner = /\\.|[^\\\n\r_]|(?:\r?\n|\r)(?!\r?\n|\r)/.source;
+	var inner = /(?:\\.|[^\\\n\r]|(?:\r?\n|\r)(?!\r?\n|\r))/.source;
 
 	/**
 	 * This function is intended for the creation of the bold or italic pattern.
@@ -124,7 +124,7 @@
 			// __strong__
 
 			// allow one nested instance of italic text using the same delimiter
-			pattern: createInline(/__(?:<inner>|_(?:<inner>)+_)+__/.source, true),
+			pattern: createInline(/__(?:(?!_)<inner>|_(?:(?!_)<inner>)+_)+__/.source, true),
 			lookbehind: true,
 			greedy: true,
 			inside: {
@@ -141,7 +141,7 @@
 			// _em_
 
 			// allow one nested instance of bold text using the same delimiter
-			pattern: createInline(/_(?:<inner>|__(?:<inner>)+__)+_/.source, true),
+			pattern: createInline(/_(?:(?!_)<inner>|__(?:(?!_)<inner>)+__)+_/.source, true),
 			lookbehind: true,
 			greedy: true,
 			inside: {
@@ -156,9 +156,7 @@
 		'strike': {
 			// ~~strike through~~
 			// ~strike~
-
-			// extra _ is because the inner pattern intentionally doesn't include it because of bold and italic
-			pattern: createInline(/(~~?)(?:<inner>|_)+?\2/.source, false),
+			pattern: createInline(/(~~?)(?:(?!~)<inner>)+?\2/.source, false),
 			lookbehind: true,
 			greedy: true,
 			inside: {
@@ -172,13 +170,21 @@
 		},
 		'url': {
 			// [example](http://example.com "Optional title")
+			// [example][id]
 			// [example] [id]
-			pattern: /!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,
+			pattern: createInline(/!?\[(?:(?!\])<inner>)+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[(?:(?!\])<inner>)+\])/.source, false),
+			lookbehind: true,
+			greedy: true,
 			inside: {
 				'variable': {
-					pattern: /(!?\[)[^\]]+(?=\]$)/,
+					pattern: /(\[)[^\]]+(?=\]$)/,
 					lookbehind: true
 				},
+				'content': {
+					pattern: /(^!?\[)[^\]]+(?=\])/,
+					lookbehind: true,
+					inside: {} // see below
+				},
 				'string': {
 					pattern: /"(?:\\.|[^"\\])*"(?=\)$)/
 				}
@@ -186,7 +192,7 @@
 		}
 	});
 
-	['bold', 'italic', 'strike'].forEach(function (token) {
+	['url', 'bold', 'italic', 'strike'].forEach(function (token) {
 		['url', 'bold', 'italic', 'strike'].forEach(function (inside) {
 			if (token !== inside) {
 				Prism.languages.markdown[token].inside.content.inside[inside] = Prism.languages.markdown[inside];
diff --git a/components/prism-markdown.min.js b/components/prism-markdown.min.js
index fd5615f..64693b9 100644
--- a/components/prism-markdown.min.js
+++ b/components/prism-markdown.min.js
@@ -1 +1 @@
-!function(s){function n(n,e){return n=n.replace(/<inner>/g,"\\\\.|[^\\\\\\n\r_]|(?:\r?\n|\r)(?!\r?\n|\r)"),e&&(n=n+"|"+n.replace(/_/g,"\\*")),RegExp("((?:^|[^\\\\])(?:\\\\{2})*)(?:"+n+")")}s.languages.markdown=s.languages.extend("markup",{}),s.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\r?\n|\r))[\s\S]+?(?=(?:\r?\n|\r)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n("__(?:<inner>|_(?:<inner>)+_)+__",!0),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n("_(?:<inner>|__(?:<inner>)+__)+_",!0),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n("(~~?)(?:<inner>|_)+?\\2",!1),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),["bold","italic","strike"].forEach(function(e){["url","bold","italic","strike"].forEach(function(n){e!==n&&(s.languages.markdown[e].inside.content.inside[n]=s.languages.markdown[n])})}),s.hooks.add("after-tokenize",function(n){"markdown"!==n.language&&"md"!==n.language||!function n(e){if(e&&"string"!=typeof e)for(var t=0,a=e.length;t<a;t++){var i=e[t];if("code"===i.type){var o=i.content[1],r=i.content[3];if(o&&r&&"code-language"===o.type&&"code-block"===r.type&&"string"==typeof o.content){var l="language-"+o.content.trim().split(/\s+/)[0].toLowerCase();r.alias?"string"==typeof r.alias?r.alias=[r.alias,l]:r.alias.push(l):r.alias=[l]}}else n(i.content)}}(n.tokens)}),s.hooks.add("wrap",function(n){if("code-block"===n.type){for(var e="",t=0,a=n.classes.length;t<a;t++){var i=n.classes[t],o=/language-(.+)/.exec(i);if(o){e=o[1];break}}var r=s.languages[e];if(r){var l=n.content.replace(/&lt;/g,"<").replace(/&amp;/g,"&");n.content=s.highlight(l,r,e)}}}),s.languages.md=s.languages.markdown}(Prism);
\ No newline at end of file
+!function(s){function n(n,e){return n=n.replace(/<inner>/g,"(?:\\\\.|[^\\\\\\n\r]|(?:\r?\n|\r)(?!\r?\n|\r))"),e&&(n=n+"|"+n.replace(/_/g,"\\*")),RegExp("((?:^|[^\\\\])(?:\\\\{2})*)(?:"+n+")")}s.languages.markdown=s.languages.extend("markup",{}),s.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\r?\n|\r))[\s\S]+?(?=(?:\r?\n|\r)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n("__(?:(?!_)<inner>|_(?:(?!_)<inner>)+_)+__",!0),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n("_(?:(?!_)<inner>|__(?:(?!_)<inner>)+__)+_",!0),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n("(~~?)(?:(?!~)<inner>)+?\\2",!1),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},url:{pattern:n('!?\\[(?:(?!\\])<inner>)+\\](?:\\([^\\s)]+(?:[\t ]+"(?:\\\\.|[^"\\\\])*")?\\)| ?\\[(?:(?!\\])<inner>)+\\])',!1),lookbehind:!0,greedy:!0,inside:{variable:{pattern:/(\[)[^\]]+(?=\]$)/,lookbehind:!0},content:{pattern:/(^!?\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),["url","bold","italic","strike"].forEach(function(e){["url","bold","italic","strike"].forEach(function(n){e!==n&&(s.languages.markdown[e].inside.content.inside[n]=s.languages.markdown[n])})}),s.hooks.add("after-tokenize",function(n){"markdown"!==n.language&&"md"!==n.language||!function n(e){if(e&&"string"!=typeof e)for(var t=0,a=e.length;t<a;t++){var i=e[t];if("code"===i.type){var o=i.content[1],r=i.content[3];if(o&&r&&"code-language"===o.type&&"code-block"===r.type&&"string"==typeof o.content){var l="language-"+o.content.trim().split(/\s+/)[0].toLowerCase();r.alias?"string"==typeof r.alias?r.alias=[r.alias,l]:r.alias.push(l):r.alias=[l]}}else n(i.content)}}(n.tokens)}),s.hooks.add("wrap",function(n){if("code-block"===n.type){for(var e="",t=0,a=n.classes.length;t<a;t++){var i=n.classes[t],o=/language-(.+)/.exec(i);if(o){e=o[1];break}}var r=s.languages[e];if(r){var l=n.content.replace(/&lt;/g,"<").replace(/&amp;/g,"&");n.content=s.highlight(l,r,e)}}}),s.languages.md=s.languages.markdown}(Prism);
\ No newline at end of file
diff --git a/known-failures.html b/known-failures.html
index ebd401e..e59e368 100644
--- a/known-failures.html
+++ b/known-failures.html
@@ -153,18 +153,6 @@ not supported *)</code></pre>
 </section>
 
 
-<section class="language-markdown">
-
-<h3>Nesting of elements is not fully supported</h3>
-<pre><code>[Link partially *italic* DOESN'T work](http://example.com)
-_ [But link inside italic DOES work](http://example.com) _
-
-[Link partially **bold** DOESN'T work](http://example.com)
-__ [But link inside bold DOES work](http://example.com) __</code></pre>
-
-</section>
-
-
 <section class="language-nasm">
 
 <h3>Numbers with underscores</h3>
diff --git a/tests/languages/markdown/bold_feature.test b/tests/languages/markdown/bold_feature.test
index db915b9..39217ab 100644
--- a/tests/languages/markdown/bold_feature.test
+++ b/tests/languages/markdown/bold_feature.test
@@ -113,7 +113,11 @@ __foo[bar](baz)__
 		["content", [
 			"foo",
 			["url", [
-				"[bar](baz)"
+				"[",
+				["content", [
+					"bar"
+				]],
+				"](baz)"
 			]]
 		]],
 		["punctuation", "__"]
@@ -183,7 +187,11 @@ __foo[bar](baz)__
 		["content", [
 			"foo",
 			["url", [
-				"[bar](baz)"
+				"[",
+				["content", [
+					"bar"
+				]],
+				"](baz)"
 			]]
 		]],
 		["punctuation", "**"]
diff --git a/tests/languages/markdown/italic_feature.test b/tests/languages/markdown/italic_feature.test
index fc9c6c8..f090260 100644
--- a/tests/languages/markdown/italic_feature.test
+++ b/tests/languages/markdown/italic_feature.test
@@ -113,7 +113,11 @@ _foo[bar](baz)_
 		["content", [
 			"foo",
 			["url", [
-				"[bar](baz)"
+				"[",
+				["content", [
+					"bar"
+				]],
+				"](baz)"
 			]]
 		]],
 		["punctuation", "_"]
@@ -183,7 +187,11 @@ _foo[bar](baz)_
 		["content", [
 			"foo",
 			["url", [
-				"[bar](baz)"
+				"[",
+				["content", [
+					"bar"
+				]],
+				"](baz)"
 			]]
 		]],
 		["punctuation", "*"]
diff --git a/tests/languages/markdown/strike_feature.test b/tests/languages/markdown/strike_feature.test
index 414fb8b..9cb9b70 100644
--- a/tests/languages/markdown/strike_feature.test
+++ b/tests/languages/markdown/strike_feature.test
@@ -113,7 +113,11 @@ bar~
 		["content", [
 			"foo",
 			["url", [
-				"[bar](baz)"
+				"[",
+				["content", [
+					"bar"
+				]],
+				"](baz)"
 			]]
 		]],
 		["punctuation", "~"]
@@ -183,7 +187,11 @@ bar~
 		["content", [
 			"foo",
 			["url", [
-				"[bar](baz)"
+				"[",
+				["content", [
+					"bar"
+				]],
+				"](baz)"
 			]]
 		]],
 		["punctuation", "~~"]
diff --git a/tests/languages/markdown/url_feature.test b/tests/languages/markdown/url_feature.test
index ca8b9cf..455009f 100644
--- a/tests/languages/markdown/url_feature.test
+++ b/tests/languages/markdown/url_feature.test
@@ -1,25 +1,154 @@
 [foo](http://prismjs.com)
 ![foo](http://prismjs.com "Foo\"bar")
+[foo][bar]
 [foo] [bar]
 
+[foo
+bar](http://prismjs.com)
+
+[foo *bar* baz](http://prismjs.com)
+[foo _bar_ baz](http://prismjs.com)
+[foo **bar** baz](http://prismjs.com)
+[foo __bar__ baz](http://prismjs.com)
+[foo ~bar~ baz](http://prismjs.com)
+[foo ~~bar~~ baz](http://prismjs.com)
+
 ----------------------------------------------------
 
 [
 	["url", [
-		"[foo](http://prismjs.com)"
+		"[",
+		["content", [
+			"foo"
+		]],
+		"](http://prismjs.com)"
 	]],
 	["url", [
-		"![foo](http://prismjs.com ",
+		"![",
+		["content", [
+			"foo"
+		]],
+		"](http://prismjs.com ",
 		["string", "\"Foo\\\"bar\""],
 		")"
 	]],
 	["url", [
-		"[foo] [",
+		"[",
+		["content", [
+			"foo"
+		]],
+		"][",
+		["variable", "bar"],
+		"]"
+	]],
+	["url", [
+		"[",
+		["content", [
+			"foo"
+		]],
+		"] [",
 		["variable", "bar"],
 		"]"
+	]],
+	["url", [
+		"[",
+		["content", [
+			"foo\r\nbar"
+		]],
+		"](http://prismjs.com)"
+	]],
+	["url", [
+		"[",
+		["content", [
+			"foo ",
+			["italic", [
+				["punctuation", "*"],
+				["content", [
+					"bar"
+				]],
+				["punctuation", "*"]
+			]],
+			" baz"
+		]],
+		"](http://prismjs.com)"
+	]],
+	["url", [
+		"[",
+		["content", [
+			"foo ",
+			["italic", [
+				["punctuation", "_"],
+				["content", [
+					"bar"
+				]],
+				["punctuation", "_"]
+			]],
+			" baz"
+		]],
+		"](http://prismjs.com)"
+	]],
+	["url", [
+		"[",
+		["content", [
+			"foo ",
+			["bold", [
+				["punctuation", "**"],
+				["content", [
+					"bar"
+				]],
+				["punctuation", "**"]
+			]],
+			" baz"
+		]],
+		"](http://prismjs.com)"
+	]],
+	["url", [
+		"[",
+		["content", [
+			"foo ",
+			["bold", [
+				["punctuation", "__"],
+				["content", [
+					"bar"
+				]],
+				["punctuation", "__"]
+			]],
+			" baz"
+		]],
+		"](http://prismjs.com)"
+	]],
+	["url", [
+		"[",
+		["content", [
+			"foo ",
+			["strike", [
+				["punctuation", "~"],
+				["content", [
+					"bar"
+				]],
+				["punctuation", "~"]
+			]],
+			" baz"
+		]],
+		"](http://prismjs.com)"
+	]],
+	["url", [
+		"[",
+		["content", [
+			"foo ",
+			["strike", [
+				["punctuation", "~~"],
+				["content", [
+					"bar"
+				]],
+				["punctuation", "~~"]
+			]],
+			" baz"
+		]],
+		"](http://prismjs.com)"
 	]]
 ]
 
 ----------------------------------------------------
 
-Checks for URLs.
\ No newline at end of file
+Checks for URLs.