From e86ec01f8924a2bd77fe8c82f28441f81c7eae21 Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Thu, 7 Jan 2016 10:39:37 +0100 Subject: [PATCH 01/22] Add whitespace-normalizer plugin This plugin normalizes whitespace in code blocks. It can perform various operations. The user can configure them through the plugin object located at Prism.plugins.NormalizeWhitespace. Prism.plugins.NormalizeWhitespace.setDefaults({ 'remove-trailing': true, 'remove-indent': true, 'left-trim': true, 'right-trim': true, /*'indent': 2, 'remove-initial-line-feed': false, 'tabs-to-spaces': 4, 'spaces-to-tabs': 4*/ }); The plugin can be disabled for certain code blocks, by adding the class "no-whitespace-normalization" to it. There is also a HTML-interface using the parse-settings plugin. --- components.js | 5 + plugins/normalize-whitespace/demo.html | 29 ++++ plugins/normalize-whitespace/index.html | 101 ++++++++++++++ .../prism-normalize-whitespace.js | 129 ++++++++++++++++++ .../prism-normalize-whitespace.min.js | 1 + plugins/remove-initial-line-feed/index.html | 3 +- 6 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 plugins/normalize-whitespace/demo.html create mode 100644 plugins/normalize-whitespace/index.html create mode 100644 plugins/normalize-whitespace/prism-normalize-whitespace.js create mode 100644 plugins/normalize-whitespace/prism-normalize-whitespace.min.js diff --git a/components.js b/components.js index b407c14b41..6c86766bdc 100644 --- a/components.js +++ b/components.js @@ -607,6 +607,11 @@ var components = { "command-line": { "title": "Command Line", "owner": "chriswells0" + }, + "normalize-whitespace": { + "title": "Normalize Whitespace", + "owner": "zeitgeist87", + "noCSS": true } } }; diff --git a/plugins/normalize-whitespace/demo.html b/plugins/normalize-whitespace/demo.html new file mode 100644 index 0000000000..34e06f47c3 --- /dev/null +++ b/plugins/normalize-whitespace/demo.html @@ -0,0 +1,29 @@ + +
+ +
+
+	
+
+
+		var example = {
+			foo: true,
+
+			bar: false
+		};
+
+
+	
+
+
+ +
+ + + \ No newline at end of file diff --git a/plugins/normalize-whitespace/index.html b/plugins/normalize-whitespace/index.html new file mode 100644 index 0000000000..02a000f180 --- /dev/null +++ b/plugins/normalize-whitespace/index.html @@ -0,0 +1,101 @@ + + + + + + + Normalize Whitespace ▲ Prism plugins + + + + + + + + + + +
+
+ +

Normalize Whitespace

+

Supports multiple operations to normalize whitespace in code blocks.

+
+ +
+

How to use

+ +

Obviously, this is supposed to work only for code blocks (<pre><code>) and not for inline code.

+

By default the plugin trims all leading and trailing whitespace of every code block. + It also removes extra indents and trailing whitespace on every line.

+ +

The plugin uses the Parse Settings plugin to get its settings. + The default settings can be overridden with the setDefaults() method + like so:

+ +

+Prism.plugins.NormalizeWhitespace.setDefaults({
+	'remove-trailing': true,
+	'remove-indent': true,
+	'left-trim': true,
+	'right-trim': true,
+	/*'indent': 2,
+	'remove-initial-line-feed': false,
+	'tabs-to-spaces': 4,
+	'spaces-to-tabs': 4*/
+});
+
+ +

It is also possible to change the default settings with data-*-attributes.

+ +
<code data-left-trim="false">...</code>
+ +

The class no-whitespace-normalization or the corresponding + data-attribute can be used to disable the normalization for a particular + code block.

+ +
<code class="no-whitespace-normalization"
+	data-whitespace-normalization="false">
+...
+</code>
+
+ +
+

Examples

+ +

The following example shows some of the possible ways of configuring this plugin:

+ +

+
+	

The result looks like this:

+ +
+
+	
+
+
+		var example = {
+			foo: true,
+
+			bar: false
+		};
+
+
+	
+
+
+ +
+ + + + + + + + + + + + + diff --git a/plugins/normalize-whitespace/prism-normalize-whitespace.js b/plugins/normalize-whitespace/prism-normalize-whitespace.js new file mode 100644 index 0000000000..f868e11498 --- /dev/null +++ b/plugins/normalize-whitespace/prism-normalize-whitespace.js @@ -0,0 +1,129 @@ +(function() { + +if (typeof self === 'undefined' || !self.Prism || !self.document) { + return; +} + +var assign = Object.assign || function (obj1, obj2) { + for (var name in obj2) { + if (obj2.hasOwnProperty(name)) + obj1[name] = obj2[name]; + } + return obj1; +} + +function NormalizeWhitespace(defaults) { + this.defaults = assign({}, defaults); +} + +function toCamelCase(value) { + return value.replace(/-(\w)/g, function(match, firstChar) { + return firstChar.toUpperCase(); + }); +} + +NormalizeWhitespace.prototype = { + setDefaults: function (defaults) { + this.defaults = assign(this.defaults, defaults); + }, + normalize: function (input, settings) { + settings = assign(this.defaults, settings); + + for (var name in settings) { + var methodName = toCamelCase(name); + if (name !== "normalize" && methodName !== 'setDefaults' && + settings[name] && this[methodName]) { + input = this[methodName].call(this, input, settings[name]); + } + } + + return input; + }, + + /* + * Normalization methods + */ + leftTrim: function (input) { + return input.replace(/^\s+/, ''); + }, + rightTrim: function (input) { + return input.replace(/\s+$/, ''); + }, + tabsToSpaces: function (input, spaces) { + spaces = spaces|0 || 4; + return input.replace(/\t/g, new Array(++spaces).join(' ')); + }, + spacesToTabs: function (input, spaces) { + spaces = spaces|0 || 4; + return input.replace(new RegExp(' {' + spaces + '}', 'g'), '\t'); + }, + removeTrailing: function (input) { + return input.replace(/\s*?$/gm, ''); + }, + // Support for deprecated plugin remove-initial-line-feed + removeInitialLineFeed: function (input) { + return input.replace(/^(?:\r?\n|\r)/, ''); + }, + removeIndent: function (input) { + var indents = input.match(/^[^\S\n\r]*(?=\S)/gm); + + if (!indents || !indents[0].length) + return input; + + indents.sort(function(a, b){return a.length - b.length; }); + + if (!indents[0].length) + return input; + + return input.replace(new RegExp('^' + indents[0], 'gm'), ''); + }, + indent: function (input, tabs) { + return input.replace(/^[^\S\n\r]*(?=\S)/gm, new Array(++tabs).join('\t') + '$&'); + } +}; + +Prism.plugins.NormalizeWhitespace = new NormalizeWhitespace({ + 'remove-trailing': true, + 'remove-indent': true, + 'left-trim': true, + 'right-trim': true, + /*'indent': 2, + 'remove-initial-line-feed': false, + 'tabs-to-spaces': 4, + 'spaces-to-tabs': 4*/ +}); + +Prism.hooks.add('before-highlight', function (env) { + var pre = env.element.parentNode; + if (!env.code || !pre || pre.nodeName.toLowerCase() !== 'pre' || + (env.settings && env.settings['whitespace-normalization'] === false)) + return; + + var children = pre.childNodes, + before = '', + after = '', + codeFound = false; + + // Move surrounding whitespace from the
 tag into the  tag
+	for (var i = 0; i < children.length; ++i) {
+		var node = children[i];
+
+		if (node == env.element) {
+			codeFound = true;
+		} else if (node.nodeName === "#text") {
+			if (codeFound) {
+				after += node.nodeValue;
+			} else {
+				before += node.nodeValue;
+			}
+
+			pre.removeChild(node);
+			--i;
+		}
+	}
+	env.code = before + env.code + after;
+
+	env.code = Prism.plugins.NormalizeWhitespace.normalize(env.code, env.settings);
+});
+
+}());
\ No newline at end of file
diff --git a/plugins/normalize-whitespace/prism-normalize-whitespace.min.js b/plugins/normalize-whitespace/prism-normalize-whitespace.min.js
new file mode 100644
index 0000000000..d0131cfa74
--- /dev/null
+++ b/plugins/normalize-whitespace/prism-normalize-whitespace.min.js
@@ -0,0 +1 @@
+!function(){function e(e){this.defaults=t({},e)}function n(e){return e.replace(/-(\w)/g,function(e,n){return n.toUpperCase()})}if("undefined"!=typeof self&&self.Prism&&self.document){var t=Object.assign||function(e,n){for(var t in n)n.hasOwnProperty(t)&&(e[t]=n[t]);return e};e.prototype={setDefaults:function(e){this.defaults=t(this.defaults,e)},normalize:function(e,r){r=t(this.defaults,r);for(var i in r){var o=n(i);"normalize"!==i&&"setDefaults"!==o&&r[i]&&this[o]&&(e=this[o].call(this,e,r[i]))}return e},leftTrim:function(e){return e.replace(/^\s+/,"")},rightTrim:function(e){return e.replace(/\s+$/,"")},tabsToSpaces:function(e,n){return n=0|n||4,e.replace(/\t/g,new Array(++n).join(" "))},spacesToTabs:function(e,n){return n=0|n||4,e.replace(new RegExp(" {"+n+"}","g"),"	")},removeTrailing:function(e){return e.replace(/\s*?$/gm,"")},removeInitialLineFeed:function(e){return e.replace(/^(?:\r?\n|\r)/,"")},removeIndent:function(e){var n=e.match(/^[^\S\n\r]*(?=\S)/gm);return n&&n[0].length?(n.sort(function(e,n){return e.length-n.length}),n[0].length?e.replace(new RegExp("^"+n[0],"gm"),""):e):e},indent:function(e,n){return e.replace(/^[^\S\n\r]*(?=\S)/gm,new Array(++n).join("	")+"$&")}},Prism.plugins.NormalizeWhitespace=new e({"remove-trailing":!0,"remove-indent":!0,"left-trim":!0,"right-trim":!0}),Prism.hooks.add("before-highlight",function(e){var n=e.element.parentNode;if(e.code&&n&&"pre"===n.nodeName.toLowerCase()&&(!e.settings||e.settings["whitespace-normalization"]!==!1)){for(var t=n.childNodes,r="",i="",o=!1,a=0;aRemove initial line feed
 
 
 
-

How to use

+

How to use (DEPRECATED)

+

This plugin will be removed in the future. Please use the general purpose Normalize Whitespace plugin instead.

Obviously, this is supposed to work only for code blocks (<pre><code>) and not for inline code.

With this plugin included, any initial line feed will be removed by default.

To bypass this behaviour, you may add the class keep-initial-line-feed to your desired <pre>.

From 704ba4f951418ad8c6680a47a3f0381cb7c06bbd Mon Sep 17 00:00:00 2001 From: Fernando Montoya Date: Sat, 6 Feb 2016 00:02:15 -0500 Subject: [PATCH 02/22] Fix Keep Markup plugin incorrect highlighting --- plugins/keep-markup/prism-keep-markup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/keep-markup/prism-keep-markup.js b/plugins/keep-markup/prism-keep-markup.js index 9038ed01dd..fff0b889ad 100644 --- a/plugins/keep-markup/prism-keep-markup.js +++ b/plugins/keep-markup/prism-keep-markup.js @@ -23,7 +23,7 @@ } else if(child.nodeType === 3) { // text if(!firstWhiteSpaces) { // We need to ignore the first white spaces in the code block - child.data = child.data.replace(/^(?:\r?\n|\r)/, ''); + child.data = child.data.replace(/^(?:\r\n|\r)/, ''); firstWhiteSpaces = true; } pos += child.data.length; @@ -94,4 +94,4 @@ }); } }); -}()); \ No newline at end of file +}()); From 38b7b3b6a143fb30247f4b917cb04fbc4e3d48d3 Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Sat, 6 Feb 2016 15:49:01 +0100 Subject: [PATCH 03/22] Add support for automatic line breaks This patch adds support for automatic line breaks after a specific number of characters. The default is 80 characters, but it can be set to any number. --- .../prism-normalize-whitespace.js | 35 ++++++++++++++++++- .../prism-normalize-whitespace.min.js | 2 +- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/plugins/normalize-whitespace/prism-normalize-whitespace.js b/plugins/normalize-whitespace/prism-normalize-whitespace.js index f868e11498..f2df35f638 100644 --- a/plugins/normalize-whitespace/prism-normalize-whitespace.js +++ b/plugins/normalize-whitespace/prism-normalize-whitespace.js @@ -22,6 +22,15 @@ function toCamelCase(value) { }); } +function tabLen(str) { + var res = 0; + for (var i = 0; i < str.length; ++i) { + if (str.charCodeAt(i) == '\t'.charCodeAt(0)) + res += 3; + } + return str.length + res; +} + NormalizeWhitespace.prototype = { setDefaults: function (defaults) { this.defaults = assign(this.defaults, defaults); @@ -79,6 +88,29 @@ NormalizeWhitespace.prototype = { }, indent: function (input, tabs) { return input.replace(/^[^\S\n\r]*(?=\S)/gm, new Array(++tabs).join('\t') + '$&'); + }, + breakLines: function (input, characters) { + characters = (characters === true) ? 80 : characters|0 || 80; + + var lines = input.split('\n'); + for (var i = 0; i < lines.length; ++i) { + if (tabLen(lines[i]) <= characters) + continue; + + var line = lines[i].split(/(\s+)/g), + len = 0; + + for (var j = 0; j < line.length; ++j) { + var tl = tabLen(line[j]); + len += tl; + if (len > characters) { + line[j] = '\n' + line[j]; + len = tl; + } + } + lines[i] = line.join(''); + } + return lines.join('\n'); } }; @@ -87,7 +119,8 @@ Prism.plugins.NormalizeWhitespace = new NormalizeWhitespace({ 'remove-indent': true, 'left-trim': true, 'right-trim': true, - /*'indent': 2, + /*'break-lines': 80, + 'indent': 2, 'remove-initial-line-feed': false, 'tabs-to-spaces': 4, 'spaces-to-tabs': 4*/ diff --git a/plugins/normalize-whitespace/prism-normalize-whitespace.min.js b/plugins/normalize-whitespace/prism-normalize-whitespace.min.js index d0131cfa74..11669d7dd8 100644 --- a/plugins/normalize-whitespace/prism-normalize-whitespace.min.js +++ b/plugins/normalize-whitespace/prism-normalize-whitespace.min.js @@ -1 +1 @@ -!function(){function e(e){this.defaults=t({},e)}function n(e){return e.replace(/-(\w)/g,function(e,n){return n.toUpperCase()})}if("undefined"!=typeof self&&self.Prism&&self.document){var t=Object.assign||function(e,n){for(var t in n)n.hasOwnProperty(t)&&(e[t]=n[t]);return e};e.prototype={setDefaults:function(e){this.defaults=t(this.defaults,e)},normalize:function(e,r){r=t(this.defaults,r);for(var i in r){var o=n(i);"normalize"!==i&&"setDefaults"!==o&&r[i]&&this[o]&&(e=this[o].call(this,e,r[i]))}return e},leftTrim:function(e){return e.replace(/^\s+/,"")},rightTrim:function(e){return e.replace(/\s+$/,"")},tabsToSpaces:function(e,n){return n=0|n||4,e.replace(/\t/g,new Array(++n).join(" "))},spacesToTabs:function(e,n){return n=0|n||4,e.replace(new RegExp(" {"+n+"}","g")," ")},removeTrailing:function(e){return e.replace(/\s*?$/gm,"")},removeInitialLineFeed:function(e){return e.replace(/^(?:\r?\n|\r)/,"")},removeIndent:function(e){var n=e.match(/^[^\S\n\r]*(?=\S)/gm);return n&&n[0].length?(n.sort(function(e,n){return e.length-n.length}),n[0].length?e.replace(new RegExp("^"+n[0],"gm"),""):e):e},indent:function(e,n){return e.replace(/^[^\S\n\r]*(?=\S)/gm,new Array(++n).join(" ")+"$&")}},Prism.plugins.NormalizeWhitespace=new e({"remove-trailing":!0,"remove-indent":!0,"left-trim":!0,"right-trim":!0}),Prism.hooks.add("before-highlight",function(e){var n=e.element.parentNode;if(e.code&&n&&"pre"===n.nodeName.toLowerCase()&&(!e.settings||e.settings["whitespace-normalization"]!==!1)){for(var t=n.childNodes,r="",i="",o=!1,a=0;an&&(o[s]="\n"+o[s],a=l)}t[i]=o.join("")}return t.join("\n")}},Prism.plugins.NormalizeWhitespace=new e({"remove-trailing":!0,"remove-indent":!0,"left-trim":!0,"right-trim":!0}),Prism.hooks.add("before-highlight",function(e){var n=e.element.parentNode;if(e.code&&n&&"pre"===n.nodeName.toLowerCase()&&(!e.settings||e.settings["whitespace-normalization"]!==!1)){for(var r=n.childNodes,t="",i="",o=!1,a=0;a Date: Sat, 6 Feb 2016 16:21:04 -0500 Subject: [PATCH 04/22] Removed firstWhiteSpaces code --- plugins/keep-markup/prism-keep-markup.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/keep-markup/prism-keep-markup.js b/plugins/keep-markup/prism-keep-markup.js index fff0b889ad..0e20c1e9f9 100644 --- a/plugins/keep-markup/prism-keep-markup.js +++ b/plugins/keep-markup/prism-keep-markup.js @@ -5,7 +5,6 @@ } Prism.hooks.add('before-highlight', function (env) { - var firstWhiteSpaces = false; var pos = 0; var data = []; var f = function (elt, baseNode) { @@ -21,11 +20,6 @@ if (child.nodeType === 1) { // element f(child); } else if(child.nodeType === 3) { // text - if(!firstWhiteSpaces) { - // We need to ignore the first white spaces in the code block - child.data = child.data.replace(/^(?:\r\n|\r)/, ''); - firstWhiteSpaces = true; - } pos += child.data.length; } } From eb68eb833d5abd68a2405c8ad27d23790c121707 Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Sun, 7 Feb 2016 18:53:04 +0100 Subject: [PATCH 05/22] Update CHANGELOG and run gulp --- CHANGELOG.md | 11 +++++++++++ plugins/keep-markup/prism-keep-markup.min.js | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e2d958bc..9b4736a178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Prism Changelog +## Unreleased + +### Updated components + +* __Keep Markup__: + * Fix Keep Markup plugin incorrect highlighting ([#880](https://github.com/PrismJS/prism/pull/880)) [[`24841ef`](https://github.com/PrismJS/prism/commit/24841ef)] + +### New plugins + +* __Normalize Whitespace__ ([#847](https://github.com/PrismJS/prism/pull/847)) [[`e86ec01`](https://github.com/PrismJS/prism/commit/e86ec01)] + ## 1.4.1 (2016-02-03) ### Other changes diff --git a/plugins/keep-markup/prism-keep-markup.min.js b/plugins/keep-markup/prism-keep-markup.min.js index 86a39c93b7..b387be7f83 100644 --- a/plugins/keep-markup/prism-keep-markup.min.js +++ b/plugins/keep-markup/prism-keep-markup.min.js @@ -1 +1 @@ -!function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.createRange&&(Prism.hooks.add("before-highlight",function(e){var n=!1,o=0,t=[],d=function(e,a){var r={};a||(r.clone=e.cloneNode(!1),r.posOpen=o,t.push(r));for(var s=0,p=e.childNodes.length;p>s;s++){var l=e.childNodes[s];1===l.nodeType?d(l):3===l.nodeType&&(n||(l.data=l.data.replace(/^(?:\r?\n|\r)/,""),n=!0),o+=l.data.length)}a||(r.posClose=o)};d(e.element,!0),t&&t.length&&(e.keepMarkup=t)}),Prism.hooks.add("after-highlight",function(e){if(e.keepMarkup&&e.keepMarkup.length){var n=function(e,o){for(var t=0,d=e.childNodes.length;d>t;t++){var a=e.childNodes[t];if(1===a.nodeType){if(!n(a,o))return!1}else 3===a.nodeType&&(!o.nodeStart&&o.pos+a.data.length>o.node.posOpen&&(o.nodeStart=a,o.nodeStartPos=o.node.posOpen-o.pos),o.nodeStart&&o.pos+a.data.length>=o.node.posClose&&(o.nodeEnd=a,o.nodeEndPos=o.node.posClose-o.pos),o.pos+=a.data.length);if(o.nodeStart&&o.nodeEnd){var r=document.createRange();return r.setStart(o.nodeStart,o.nodeStartPos),r.setEnd(o.nodeEnd,o.nodeEndPos),o.node.clone.appendChild(r.extractContents()),r.insertNode(o.node.clone),r.detach(),!1}}return!0};e.keepMarkup.forEach(function(o){n(e.element,{node:o,pos:0})})}}))}(); \ No newline at end of file +!function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.createRange&&(Prism.hooks.add("before-highlight",function(e){var n=0,o=[],t=function(e,d){var a={};d||(a.clone=e.cloneNode(!1),a.posOpen=n,o.push(a));for(var r=0,s=e.childNodes.length;s>r;r++){var p=e.childNodes[r];1===p.nodeType?t(p):3===p.nodeType&&(n+=p.data.length)}d||(a.posClose=n)};t(e.element,!0),o&&o.length&&(e.keepMarkup=o)}),Prism.hooks.add("after-highlight",function(e){if(e.keepMarkup&&e.keepMarkup.length){var n=function(e,o){for(var t=0,d=e.childNodes.length;d>t;t++){var a=e.childNodes[t];if(1===a.nodeType){if(!n(a,o))return!1}else 3===a.nodeType&&(!o.nodeStart&&o.pos+a.data.length>o.node.posOpen&&(o.nodeStart=a,o.nodeStartPos=o.node.posOpen-o.pos),o.nodeStart&&o.pos+a.data.length>=o.node.posClose&&(o.nodeEnd=a,o.nodeEndPos=o.node.posClose-o.pos),o.pos+=a.data.length);if(o.nodeStart&&o.nodeEnd){var r=document.createRange();return r.setStart(o.nodeStart,o.nodeStartPos),r.setEnd(o.nodeEnd,o.nodeEndPos),o.node.clone.appendChild(r.extractContents()),r.insertNode(o.node.clone),r.detach(),!1}}return!0};e.keepMarkup.forEach(function(o){n(e.element,{node:o,pos:0})})}}))}(); \ No newline at end of file From 6880b3c1c78d832e861691dc5c162e4f22cd7e0b Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Wed, 10 Feb 2016 11:08:44 +0100 Subject: [PATCH 06/22] Preserve Markup in Normalize-Whitespace plugin Since the Normalize-Whitespace plugin is only concerned with whitespace, it is easy to preserve the markup for other plugins like keep-markup. --- plugins/normalize-whitespace/index.html | 35 ++++++++++++++++++- .../prism-normalize-whitespace.js | 14 ++++++-- .../prism-normalize-whitespace.min.js | 2 +- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/plugins/normalize-whitespace/index.html b/plugins/normalize-whitespace/index.html index 02a000f180..e448e4d2d6 100644 --- a/plugins/normalize-whitespace/index.html +++ b/plugins/normalize-whitespace/index.html @@ -8,6 +8,19 @@ + + @@ -39,7 +52,8 @@

How to use

'remove-indent': true, 'left-trim': true, 'right-trim': true, - /*'indent': 2, + /*'break-lines': 80, + 'indent': 2, 'remove-initial-line-feed': false, 'tabs-to-spaces': 4, 'spaces-to-tabs': 4*/ @@ -85,6 +99,24 @@

Examples

+

It is also compatible with the keep-markup plugin if you load it after this plugin.

+ +
+
+	
+
+
+	@media screen {
+		div {
+			text-decoration: underline;
+			background: url('foo.png');
+		}
+	}
+
+
+
+ +
@@ -92,6 +124,7 @@

Examples

+ diff --git a/plugins/normalize-whitespace/prism-normalize-whitespace.js b/plugins/normalize-whitespace/prism-normalize-whitespace.js index f2df35f638..ca2796fbbf 100644 --- a/plugins/normalize-whitespace/prism-normalize-whitespace.js +++ b/plugins/normalize-whitespace/prism-normalize-whitespace.js @@ -135,7 +135,8 @@ Prism.hooks.add('before-highlight', function (env) { var children = pre.childNodes, before = '', after = '', - codeFound = false; + codeFound = false, + Normalizer = Prism.plugins.NormalizeWhitespace; // Move surrounding whitespace from the
 tag into the  tag
 	for (var i = 0; i < children.length; ++i) {
@@ -154,9 +155,16 @@ Prism.hooks.add('before-highlight', function (env) {
 			--i;
 		}
 	}
-	env.code = before + env.code + after;
 
-	env.code = Prism.plugins.NormalizeWhitespace.normalize(env.code, env.settings);
+	if (!env.element.children.length) {
+		env.code = before + env.code + after;
+		env.code = Normalizer.normalize(env.code, env.settings);
+	} else {
+		// Preserve markup for keep-markup plugin
+		var html = before + env.element.innerHTML + after;
+		env.element.innerHTML = Normalizer.normalize(html, env.settings);
+		env.code = env.element.textContent;
+	}
 });
 
 }());
\ No newline at end of file
diff --git a/plugins/normalize-whitespace/prism-normalize-whitespace.min.js b/plugins/normalize-whitespace/prism-normalize-whitespace.min.js
index 11669d7dd8..9243e5c28d 100644
--- a/plugins/normalize-whitespace/prism-normalize-whitespace.min.js
+++ b/plugins/normalize-whitespace/prism-normalize-whitespace.min.js
@@ -1 +1 @@
-!function(){function e(e){this.defaults=t({},e)}function n(e){return e.replace(/-(\w)/g,function(e,n){return n.toUpperCase()})}function r(e){for(var n=0,r=0;rn&&(o[s]="\n"+o[s],a=l)}t[i]=o.join("")}return t.join("\n")}},Prism.plugins.NormalizeWhitespace=new e({"remove-trailing":!0,"remove-indent":!0,"left-trim":!0,"right-trim":!0}),Prism.hooks.add("before-highlight",function(e){var n=e.element.parentNode;if(e.code&&n&&"pre"===n.nodeName.toLowerCase()&&(!e.settings||e.settings["whitespace-normalization"]!==!1)){for(var r=n.childNodes,t="",i="",o=!1,a=0;an&&(o[l]="\n"+o[l],a=s)}r[i]=o.join("")}return r.join("\n")}},Prism.plugins.NormalizeWhitespace=new e({"remove-trailing":!0,"remove-indent":!0,"left-trim":!0,"right-trim":!0}),Prism.hooks.add("before-highlight",function(e){var n=e.element.parentNode;if(e.code&&n&&"pre"===n.nodeName.toLowerCase()&&(!e.settings||e.settings["whitespace-normalization"]!==!1)){for(var t=n.childNodes,r="",i="",o=!1,a=Prism.plugins.NormalizeWhitespace,l=0;l
Date: Fri, 12 Feb 2016 10:53:38 +0100
Subject: [PATCH 07/22] Cleanup normalize-whitespace and improve keep-markup
 integration

This patch removes the misleading references to the parse-settings
plugin from the normalize-whitespace documentation and adds a
description for all supported operations.

The keep-markup plugin sets the property Prism.plugins.KeepMarkup
to allow the normalize-whitespace plugin to optimize its
operation.

Furthermore, the normalize-whitespace plugin makes sure, that it
is the first plugin to run on a particular code block.
---
 plugins/keep-markup/prism-keep-markup.js      |  6 +++
 plugins/keep-markup/prism-keep-markup.min.js  |  2 +-
 plugins/normalize-whitespace/demo.html        | 14 +++---
 plugins/normalize-whitespace/index.html       | 44 +++++++++++--------
 .../prism-normalize-whitespace.js             |  5 ++-
 .../prism-normalize-whitespace.min.js         |  2 +-
 6 files changed, 46 insertions(+), 27 deletions(-)

diff --git a/plugins/keep-markup/prism-keep-markup.js b/plugins/keep-markup/prism-keep-markup.js
index 0e20c1e9f9..58e20fd0d7 100644
--- a/plugins/keep-markup/prism-keep-markup.js
+++ b/plugins/keep-markup/prism-keep-markup.js
@@ -4,7 +4,13 @@
 		return;
 	}
 
+	Prism.plugins.KeepMarkup = true;
+
 	Prism.hooks.add('before-highlight', function (env) {
+		if (!env.element.children.length) {
+			return;
+		}
+
 		var pos = 0;
 		var data = [];
 		var f = function (elt, baseNode) {
diff --git a/plugins/keep-markup/prism-keep-markup.min.js b/plugins/keep-markup/prism-keep-markup.min.js
index b387be7f83..ecaf13cef9 100644
--- a/plugins/keep-markup/prism-keep-markup.min.js
+++ b/plugins/keep-markup/prism-keep-markup.min.js
@@ -1 +1 @@
-!function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.createRange&&(Prism.hooks.add("before-highlight",function(e){var n=0,o=[],t=function(e,d){var a={};d||(a.clone=e.cloneNode(!1),a.posOpen=n,o.push(a));for(var r=0,s=e.childNodes.length;s>r;r++){var p=e.childNodes[r];1===p.nodeType?t(p):3===p.nodeType&&(n+=p.data.length)}d||(a.posClose=n)};t(e.element,!0),o&&o.length&&(e.keepMarkup=o)}),Prism.hooks.add("after-highlight",function(e){if(e.keepMarkup&&e.keepMarkup.length){var n=function(e,o){for(var t=0,d=e.childNodes.length;d>t;t++){var a=e.childNodes[t];if(1===a.nodeType){if(!n(a,o))return!1}else 3===a.nodeType&&(!o.nodeStart&&o.pos+a.data.length>o.node.posOpen&&(o.nodeStart=a,o.nodeStartPos=o.node.posOpen-o.pos),o.nodeStart&&o.pos+a.data.length>=o.node.posClose&&(o.nodeEnd=a,o.nodeEndPos=o.node.posClose-o.pos),o.pos+=a.data.length);if(o.nodeStart&&o.nodeEnd){var r=document.createRange();return r.setStart(o.nodeStart,o.nodeStartPos),r.setEnd(o.nodeEnd,o.nodeEndPos),o.node.clone.appendChild(r.extractContents()),r.insertNode(o.node.clone),r.detach(),!1}}return!0};e.keepMarkup.forEach(function(o){n(e.element,{node:o,pos:0})})}}))}();
\ No newline at end of file
+!function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.createRange&&(Prism.plugins.KeepMarkup=!0,Prism.hooks.add("before-highlight",function(e){if(e.element.children.length){var n=0,o=[],t=function(e,d){var r={};d||(r.clone=e.cloneNode(!1),r.posOpen=n,o.push(r));for(var a=0,s=e.childNodes.length;s>a;a++){var p=e.childNodes[a];1===p.nodeType?t(p):3===p.nodeType&&(n+=p.data.length)}d||(r.posClose=n)};t(e.element,!0),o&&o.length&&(e.keepMarkup=o)}}),Prism.hooks.add("after-highlight",function(e){if(e.keepMarkup&&e.keepMarkup.length){var n=function(e,o){for(var t=0,d=e.childNodes.length;d>t;t++){var r=e.childNodes[t];if(1===r.nodeType){if(!n(r,o))return!1}else 3===r.nodeType&&(!o.nodeStart&&o.pos+r.data.length>o.node.posOpen&&(o.nodeStart=r,o.nodeStartPos=o.node.posOpen-o.pos),o.nodeStart&&o.pos+r.data.length>=o.node.posClose&&(o.nodeEnd=r,o.nodeEndPos=o.node.posClose-o.pos),o.pos+=r.data.length);if(o.nodeStart&&o.nodeEnd){var a=document.createRange();return a.setStart(o.nodeStart,o.nodeStartPos),a.setEnd(o.nodeEnd,o.nodeEndPos),o.node.clone.appendChild(a.extractContents()),a.insertNode(o.node.clone),a.detach(),!1}}return!0};e.keepMarkup.forEach(function(o){n(e.element,{node:o,pos:0})})}}))}();
\ No newline at end of file
diff --git a/plugins/normalize-whitespace/demo.html b/plugins/normalize-whitespace/demo.html
index 34e06f47c3..740475cbe3 100644
--- a/plugins/normalize-whitespace/demo.html
+++ b/plugins/normalize-whitespace/demo.html
@@ -1,9 +1,9 @@
-
-
+ +
 
-	
+	
 
 
 		var example = {
@@ -20,10 +20,12 @@
 
\ No newline at end of file diff --git a/plugins/normalize-whitespace/index.html b/plugins/normalize-whitespace/index.html index e448e4d2d6..7942b55edb 100644 --- a/plugins/normalize-whitespace/index.html +++ b/plugins/normalize-whitespace/index.html @@ -42,8 +42,7 @@

How to use

By default the plugin trims all leading and trailing whitespace of every code block. It also removes extra indents and trailing whitespace on every line.

-

The plugin uses the Parse Settings plugin to get its settings. - The default settings can be overridden with the setDefaults() method +

The default settings can be overridden with the setDefaults() method like so:


@@ -60,24 +59,35 @@ 

How to use

});
-

It is also possible to change the default settings with data-*-attributes.

- -
<code data-left-trim="false">...</code>
- -

The class no-whitespace-normalization or the corresponding - data-attribute can be used to disable the normalization for a particular - code block.

- -
<code class="no-whitespace-normalization"
-	data-whitespace-normalization="false">
-...
-</code>
+

The following settings are available:

+ +
+
remove-trailing
+
Removes trailing whitespace on all lines.
+
remove-indent
+
If the whole code block is indented too much it removes the extra indent.
+
left-trim
+
Removes all whitespace from the top of the code block.
+
right-trim
+
Removes all whitespace from the bottom of the code block.
+
break-lines
+
Simple way of breaking long lines at a certain length (default is 80 characters).
+
indent
+
Adds a certain number of tabs to every line.
+
remove-initial-line-feed
+
Less aggressive version of left-trim. + It only removes a single line feed from the top of the code block.
+
tabs-to-spaces
+
Converts all tabs to a certain number of spaces (default is 4 spaces).
+
spaces-to-tabs
+
Converts a certain number of spaces to a tab (default is 4 spaces).
+

Examples

-

The following example shows some of the possible ways of configuring this plugin:

+

The following example demonstrates the use of this plugin:


 
@@ -99,7 +109,7 @@ 

Examples

-

It is also compatible with the keep-markup plugin if you load it after this plugin.

+

It is also compatible with the keep-markup plugin:

 
@@ -122,13 +132,11 @@ 

Examples

- - diff --git a/plugins/normalize-whitespace/prism-normalize-whitespace.js b/plugins/normalize-whitespace/prism-normalize-whitespace.js index ca2796fbbf..bb5a78c274 100644 --- a/plugins/normalize-whitespace/prism-normalize-whitespace.js +++ b/plugins/normalize-whitespace/prism-normalize-whitespace.js @@ -156,7 +156,7 @@ Prism.hooks.add('before-highlight', function (env) { } } - if (!env.element.children.length) { + if (!env.element.children.length || !Prism.plugins.KeepMarkup) { env.code = before + env.code + after; env.code = Normalizer.normalize(env.code, env.settings); } else { @@ -166,5 +166,8 @@ Prism.hooks.add('before-highlight', function (env) { env.code = env.element.textContent; } }); +// Make sure our callback runs first +var hooks = Prism.hooks.all['before-highlight']; +hooks.unshift(hooks.pop()); }()); \ No newline at end of file diff --git a/plugins/normalize-whitespace/prism-normalize-whitespace.min.js b/plugins/normalize-whitespace/prism-normalize-whitespace.min.js index 9243e5c28d..7511b254bd 100644 --- a/plugins/normalize-whitespace/prism-normalize-whitespace.min.js +++ b/plugins/normalize-whitespace/prism-normalize-whitespace.min.js @@ -1 +1 @@ -!function(){function e(e){this.defaults=r({},e)}function n(e){return e.replace(/-(\w)/g,function(e,n){return n.toUpperCase()})}function t(e){for(var n=0,t=0;tn&&(o[l]="\n"+o[l],a=s)}r[i]=o.join("")}return r.join("\n")}},Prism.plugins.NormalizeWhitespace=new e({"remove-trailing":!0,"remove-indent":!0,"left-trim":!0,"right-trim":!0}),Prism.hooks.add("before-highlight",function(e){var n=e.element.parentNode;if(e.code&&n&&"pre"===n.nodeName.toLowerCase()&&(!e.settings||e.settings["whitespace-normalization"]!==!1)){for(var t=n.childNodes,r="",i="",o=!1,a=Prism.plugins.NormalizeWhitespace,l=0;ln&&(o[l]="\n"+o[l],a=s)}r[i]=o.join("")}return r.join("\n")}},Prism.plugins.NormalizeWhitespace=new e({"remove-trailing":!0,"remove-indent":!0,"left-trim":!0,"right-trim":!0}),Prism.hooks.add("before-highlight",function(e){var n=e.element.parentNode;if(e.code&&n&&"pre"===n.nodeName.toLowerCase()&&(!e.settings||e.settings["whitespace-normalization"]!==!1)){for(var t=n.childNodes,r="",i="",o=!1,a=Prism.plugins.NormalizeWhitespace,l=0;l Date: Fri, 12 Feb 2016 11:03:08 +0100 Subject: [PATCH 08/22] Add missing prism.js to the documentation of normalize-whitespace --- plugins/normalize-whitespace/demo.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/normalize-whitespace/demo.html b/plugins/normalize-whitespace/demo.html index 740475cbe3..a6bcf22bea 100644 --- a/plugins/normalize-whitespace/demo.html +++ b/plugins/normalize-whitespace/demo.html @@ -18,6 +18,8 @@
+ + --> diff --git a/templates/header-plugins.html b/templates/header-plugins.html index 3b33d87c88..45e2aac856 100644 --- a/templates/header-plugins.html +++ b/templates/header-plugins.html @@ -4,5 +4,5 @@

Prism plugins

Prism is a lightweight, extensible syntax highlighter, built with modern web standards in mind. - It’s a spin-off from Dabblet and is tested there daily by thousands. -

\ No newline at end of file + It’s used in thousands of websites, including some of those you visit daily. +

From eb2a8d8613c291f976c94a179b698c75e520084e Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Sat, 13 Feb 2016 15:29:46 -0500 Subject: [PATCH 12/22] =?UTF-8?q?Changed=20weight=20in=20the=20header,=20a?= =?UTF-8?q?s=20we=E2=80=99re=20now=202KB=20minified=20&=20gzipped.=20Also?= =?UTF-8?q?=20way=20overdue=20:)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index ef094181e6..522855301a 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,7 @@
  • Light as a feather - The core is 1.6KB minified & gzipped. Languages add 0.3-0.5KB each, themes are around 1KB. + The core is 2KB minified & gzipped. Languages add 0.3-0.5KB each, themes are around 1KB.
  • Blazing fast From af8da8ec978f38707ac43bb55c179cf42849cac0 Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Sun, 14 Feb 2016 21:08:14 -0500 Subject: [PATCH 13/22] =?UTF-8?q?Implemented=20@zeitgeist87=E2=80=99s=20su?= =?UTF-8?q?ggestion=20in=20#890=20re:=20env.elements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/prism-core.js | 16 ++++++++-------- components/prism-core.min.js | 2 +- prism.js | 23 +++++++++++++++-------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/components/prism-core.js b/components/prism-core.js index 9fcfff658d..0ca386ceb7 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -89,19 +89,19 @@ var _ = _self.Prism = { insertBefore: function (inside, before, insert, root) { root = root || _.languages; var grammar = root[inside]; - + if (arguments.length == 2) { insert = arguments[1]; - + for (var newToken in insert) { if (insert.hasOwnProperty(newToken)) { grammar[newToken] = insert[newToken]; } } - + return grammar; } - + var ret = {}; for (var token in grammar) { @@ -121,7 +121,7 @@ var _ = _self.Prism = { ret[token] = grammar[token]; } } - + // Update references in other language definitions _.languages.DFS(_.languages, function(key, value) { if (value === root[inside] && key != inside) { @@ -152,7 +152,7 @@ var _ = _self.Prism = { } }, plugins: {}, - + highlightAll: function(async, callback) { var env = { callback: callback, @@ -160,8 +160,8 @@ var _ = _self.Prism = { }; _.hooks.run("before-highlightall", env); - - var elements = document.querySelectorAll(env.selector); + + var elements = env.elements || document.querySelectorAll(env.selector); for (var i=0, element; element = elements[i++];) { _.highlightElement(element, async === true, env.callback); diff --git a/components/prism-core.min.js b/components/prism-core.min.js index 85faffc80b..577b2d0c74 100644 --- a/components/prism-core.min.js +++ b/components/prism-core.min.js @@ -1 +1 @@ -var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){g&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,b=y+v,k=d.slice(0,y+1),w=d.slice(b+1),_=[p,1];k&&_.push(k);var P=new a(l,c?n.tokenize(m,c):m,h);_.push(P),w&&_.push(w),Array.prototype.splice.apply(r,_)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(t)}}},a=n.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var i={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}n.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=(o?" ":"")+s+'="'+(i.attributes[s]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,i=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){g&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,b=y+v,k=d.slice(0,y+1),w=d.slice(b+1),_=[p,1];k&&_.push(k);var P=new a(i,c?n.tokenize(m,c):m,h);_.push(P),w&&_.push(w),Array.prototype.splice.apply(r,_)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file diff --git a/prism.js b/prism.js index c40a902b82..8474702d94 100644 --- a/prism.js +++ b/prism.js @@ -94,19 +94,19 @@ var _ = _self.Prism = { insertBefore: function (inside, before, insert, root) { root = root || _.languages; var grammar = root[inside]; - + if (arguments.length == 2) { insert = arguments[1]; - + for (var newToken in insert) { if (insert.hasOwnProperty(newToken)) { grammar[newToken] = insert[newToken]; } } - + return grammar; } - + var ret = {}; for (var token in grammar) { @@ -126,7 +126,7 @@ var _ = _self.Prism = { ret[token] = grammar[token]; } } - + // Update references in other language definitions _.languages.DFS(_.languages, function(key, value) { if (value === root[inside] && key != inside) { @@ -157,12 +157,19 @@ var _ = _self.Prism = { } }, plugins: {}, - + highlightAll: function(async, callback) { - var elements = document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'); + var env = { + callback: callback, + selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code' + }; + + _.hooks.run("before-highlightall", env); + + var elements = env.elements || document.querySelectorAll(env.selector); for (var i=0, element; element = elements[i++];) { - _.highlightElement(element, async === true, callback); + _.highlightElement(element, async === true, env.callback); } }, From 07d77e53a4b78dd2c26c744d13c9f0cefa302728 Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Sun, 14 Feb 2016 21:08:34 -0500 Subject: [PATCH 14/22] Added unescaped markup plugin (hidden) --- plugins/unescaped-markup/index.html | 121 ++++++++++++++++++ .../prism-unescaped-markup.css | 10 ++ .../prism-unescaped-markup.js | 32 +++++ .../prism-unescaped-markup.min.js | 1 + 4 files changed, 164 insertions(+) create mode 100644 plugins/unescaped-markup/index.html create mode 100644 plugins/unescaped-markup/prism-unescaped-markup.css create mode 100644 plugins/unescaped-markup/prism-unescaped-markup.js create mode 100644 plugins/unescaped-markup/prism-unescaped-markup.min.js diff --git a/plugins/unescaped-markup/index.html b/plugins/unescaped-markup/index.html new file mode 100644 index 0000000000..d3da35a9fc --- /dev/null +++ b/plugins/unescaped-markup/index.html @@ -0,0 +1,121 @@ + + + + + + + Keep markup ▲ Prism plugins + + + + + + + + + + + + +
    +
    + +

    Unescaped markup

    +

    Write markup without having to escape anything.

    +
    + +
    +

    How to use

    +

    Instead of using <pre><code> elements, use <script type="text/plain">:

    +
    + +
    +

    Examples

    + +

    View source to see that the following didn’t need escaping (except for </script>, that does):

    + + +
    + +
    +

    FAQ

    + +

    Why not use the HTML <template> tag?

    + +

    Because it is a PITA to get its textContent and needs to be pointlessly cloned. + Feel free to implement it yourself and send a pull request though, if you are so inclined.

    + +

    Can I use this inline?

    + +

    Not out of the box, because I figured it’s more of a hassle to type <script type="text/plain"> than escape the 1-2 < characters you need to escape in inline code. + Also inline code is not as frequently copy-pasted, which was the major source of annoyance that got me to write this plugin.

    +
    + +
    + + + + + + + + + diff --git a/plugins/unescaped-markup/prism-unescaped-markup.css b/plugins/unescaped-markup/prism-unescaped-markup.css new file mode 100644 index 0000000000..5cd92bf0be --- /dev/null +++ b/plugins/unescaped-markup/prism-unescaped-markup.css @@ -0,0 +1,10 @@ +/* Fallback, in case JS does not run, to ensure the code is at least visible */ +.lang-markup script[type='text/plain'], +.language-markup script[type='text/plain'], +script[type='text/plain'].lang-markup, +script[type='text/plain'].language-markup { + display: block; + font: 100% Consolas, Monaco, monoscpace; + white-space: pre; + overflow: auto; +} diff --git a/plugins/unescaped-markup/prism-unescaped-markup.js b/plugins/unescaped-markup/prism-unescaped-markup.js new file mode 100644 index 0000000000..1a13b8e89b --- /dev/null +++ b/plugins/unescaped-markup/prism-unescaped-markup.js @@ -0,0 +1,32 @@ +(function () { + + if (typeof self === 'undefined' || !self.Prism || !self.document || !Prism.languages.markup) { + return; + } + + Prism.plugins.UnescapedMarkup = true; + + Prism.hooks.add('before-highlightall', function (env) { console.log(env); + env.selector += ", .lang-markup script[type='text/plain'], .language-markup script[type='text/plain']" + + ", script[type='text/plain'].lang-markup, script[type='text/plain'].language-markup"; + }); + + Prism.hooks.add('before-highlight', function (env) { + if (env.language != "markup") { + return; + } + + if (env.element.matches("script[type='text/plain']")) { + var code = document.createElement("code"); + var pre = document.createElement("pre"); + + pre.className = code.className = env.element.className; + code.textContent = env.code; + + pre.appendChild(code); + env.element.parentNode.replaceChild(pre, env.element); + env.element = code; + return; + } + }); +}()); diff --git a/plugins/unescaped-markup/prism-unescaped-markup.min.js b/plugins/unescaped-markup/prism-unescaped-markup.min.js new file mode 100644 index 0000000000..faf9f9bc37 --- /dev/null +++ b/plugins/unescaped-markup/prism-unescaped-markup.min.js @@ -0,0 +1 @@ +!function(){"undefined"!=typeof self&&self.Prism&&self.document&&Prism.languages.markup&&(Prism.plugins.UnescapedMarkup=!0,Prism.hooks.add("before-highlightall",function(e){console.log(e),e.selector+=", .lang-markup script[type='text/plain'], .language-markup script[type='text/plain'], script[type='text/plain'].lang-markup, script[type='text/plain'].language-markup"}),Prism.hooks.add("before-highlight",function(e){if("markup"==e.language&&e.element.matches("script[type='text/plain']")){var t=document.createElement("code"),a=document.createElement("pre");return a.className=t.className=e.element.className,t.textContent=e.code,a.appendChild(t),e.element.parentNode.replaceChild(a,e.element),e.element=t,void 0}}))}(); \ No newline at end of file From ad30c323cd00900496882b4cc82a5ead34d4eb1c Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Sun, 14 Feb 2016 21:25:57 -0500 Subject: [PATCH 15/22] [unescaped markup] Fixed bug with escaped --- plugins/unescaped-markup/prism-unescaped-markup.js | 4 +++- plugins/unescaped-markup/prism-unescaped-markup.min.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/unescaped-markup/prism-unescaped-markup.js b/plugins/unescaped-markup/prism-unescaped-markup.js index 1a13b8e89b..9ed0e1a28b 100644 --- a/plugins/unescaped-markup/prism-unescaped-markup.js +++ b/plugins/unescaped-markup/prism-unescaped-markup.js @@ -6,7 +6,7 @@ Prism.plugins.UnescapedMarkup = true; - Prism.hooks.add('before-highlightall', function (env) { console.log(env); + Prism.hooks.add('before-highlightall', function (env) { env.selector += ", .lang-markup script[type='text/plain'], .language-markup script[type='text/plain']" + ", script[type='text/plain'].lang-markup, script[type='text/plain'].language-markup"; }); @@ -21,6 +21,8 @@ var pre = document.createElement("pre"); pre.className = code.className = env.element.className; + + env.code = env.code.replace(/<\/script(>|>)/gi, ""); code.textContent = env.code; pre.appendChild(code); diff --git a/plugins/unescaped-markup/prism-unescaped-markup.min.js b/plugins/unescaped-markup/prism-unescaped-markup.min.js index faf9f9bc37..e95fab31d1 100644 --- a/plugins/unescaped-markup/prism-unescaped-markup.min.js +++ b/plugins/unescaped-markup/prism-unescaped-markup.min.js @@ -1 +1 @@ -!function(){"undefined"!=typeof self&&self.Prism&&self.document&&Prism.languages.markup&&(Prism.plugins.UnescapedMarkup=!0,Prism.hooks.add("before-highlightall",function(e){console.log(e),e.selector+=", .lang-markup script[type='text/plain'], .language-markup script[type='text/plain'], script[type='text/plain'].lang-markup, script[type='text/plain'].language-markup"}),Prism.hooks.add("before-highlight",function(e){if("markup"==e.language&&e.element.matches("script[type='text/plain']")){var t=document.createElement("code"),a=document.createElement("pre");return a.className=t.className=e.element.className,t.textContent=e.code,a.appendChild(t),e.element.parentNode.replaceChild(a,e.element),e.element=t,void 0}}))}(); \ No newline at end of file +!function(){"undefined"!=typeof self&&self.Prism&&self.document&&Prism.languages.markup&&(Prism.plugins.UnescapedMarkup=!0,Prism.hooks.add("before-highlightall",function(e){e.selector+=", .lang-markup script[type='text/plain'], .language-markup script[type='text/plain'], script[type='text/plain'].lang-markup, script[type='text/plain'].language-markup"}),Prism.hooks.add("before-highlight",function(e){if("markup"==e.language&&e.element.matches("script[type='text/plain']")){var t=document.createElement("code"),a=document.createElement("pre");return a.className=t.className=e.element.className,e.code=e.code.replace(/<\/script(>|>)/gi,""),t.textContent=e.code,a.appendChild(t),e.element.parentNode.replaceChild(a,e.element),e.element=t,void 0}}))}(); \ No newline at end of file From e29cbe1caf0140a328b314853adb13794706339a Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Mon, 15 Feb 2016 15:47:48 -0500 Subject: [PATCH 16/22] [unescaped markup] Fix small issues @zeitgeist87 pointed out --- plugins/unescaped-markup/index.html | 2 +- plugins/unescaped-markup/prism-unescaped-markup.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/unescaped-markup/index.html b/plugins/unescaped-markup/index.html index d3da35a9fc..f29799cbcc 100644 --- a/plugins/unescaped-markup/index.html +++ b/plugins/unescaped-markup/index.html @@ -4,7 +4,7 @@ - Keep markup ▲ Prism plugins + Unescaped markup ▲ Prism plugins diff --git a/plugins/unescaped-markup/prism-unescaped-markup.js b/plugins/unescaped-markup/prism-unescaped-markup.js index 9ed0e1a28b..a8cf4e2dc7 100644 --- a/plugins/unescaped-markup/prism-unescaped-markup.js +++ b/plugins/unescaped-markup/prism-unescaped-markup.js @@ -20,7 +20,7 @@ var code = document.createElement("code"); var pre = document.createElement("pre"); - pre.className = code.className = env.element.className; + pre.className = code.className = env.element.className; env.code = env.code.replace(/<\/script(>|>)/gi, ""); code.textContent = env.code; From 54400fbaa33e005f527c37f77e401a54b8d9adcd Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Wed, 17 Feb 2016 15:26:01 +0100 Subject: [PATCH 17/22] Add property 'aliasTitles' to components.js This is necessary, because a language that has a lot of aliases like markup (HTML, SVG, XML, MathML), is only added to the languageMap once as "Markup". So any plugin that depends on the languageMap is limited by that. This patch allows the definition of additional titles for languages, depending on the alias that is used. --- components.js | 1 + gulpfile.js | 4 ++++ plugins/file-highlight/prism-file-highlight.js | 9 +++++---- plugins/file-highlight/prism-file-highlight.min.js | 2 +- plugins/show-language/prism-show-language.js | 2 +- plugins/show-language/prism-show-language.min.js | 2 +- prism.js | 9 +++++---- 7 files changed, 18 insertions(+), 11 deletions(-) diff --git a/components.js b/components.js index 80110d348a..462e259165 100644 --- a/components.js +++ b/components.js @@ -44,6 +44,7 @@ var components = { }, "markup": { "title": "Markup", + "aliasTitles": { "html": "HTML", "xml": "XML", "svg": "SVG", "mathml": "MathML" }, "option": "default" }, "css": { diff --git a/gulpfile.js b/gulpfile.js index 59966531d6..96c1317605 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -70,6 +70,10 @@ gulp.task('languages-plugins', function (cb) { languagesMap[p] = title; } + for (var name in data.languages[p].aliasTitles) { + languagesMap[name] = data.languages[p].aliasTitles[name]; + } + if(data.languages[p].require) { dependenciesMap[p] = data.languages[p].require; } diff --git a/plugins/file-highlight/prism-file-highlight.js b/plugins/file-highlight/prism-file-highlight.js index 1108f2c951..e8d6c75b15 100644 --- a/plugins/file-highlight/prism-file-highlight.js +++ b/plugins/file-highlight/prism-file-highlight.js @@ -7,13 +7,14 @@ var Extensions = { 'js': 'javascript', - 'html': 'markup', - 'svg': 'markup', - 'xml': 'markup', 'py': 'python', 'rb': 'ruby', 'ps1': 'powershell', - 'psm1': 'powershell' + 'psm1': 'powershell', + 'sh': 'bash', + 'bat': 'batch', + 'h': 'c', + 'tex': 'latex' }; if(Array.prototype.forEach) { // Check to prevent error in IE8 diff --git a/plugins/file-highlight/prism-file-highlight.min.js b/plugins/file-highlight/prism-file-highlight.min.js index 10231bfc86..84bb37447a 100644 --- a/plugins/file-highlight/prism-file-highlight.min.js +++ b/plugins/file-highlight/prism-file-highlight.min.js @@ -1 +1 @@ -!function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",html:"markup",svg:"markup",xml:"markup",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell"};Array.prototype.forEach&&Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var r,a=t.getAttribute("data-src"),n=t,s=/\blang(?:uage)?-(?!\*)(\w+)\b/i;n&&!s.test(n.className);)n=n.parentNode;if(n&&(r=(t.className.match(s)||[,""])[1]),!r){var o=(a.match(/\.(\w+)$/)||[,""])[1];r=e[o]||o}var l=document.createElement("code");l.className="language-"+r,t.textContent="",l.textContent="Loading…",t.appendChild(l);var i=new XMLHttpRequest;i.open("GET",a,!0),i.onreadystatechange=function(){4==i.readyState&&(i.status<400&&i.responseText?(l.textContent=i.responseText,Prism.highlightElement(l)):l.textContent=i.status>=400?"✖ Error "+i.status+" while fetching file: "+i.statusText:"✖ Error: File does not exist or is empty")},i.send(null)})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight))}(); \ No newline at end of file +!function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.forEach&&Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var a,s=t.getAttribute("data-src"),n=t,r=/\blang(?:uage)?-(?!\*)(\w+)\b/i;n&&!r.test(n.className);)n=n.parentNode;if(n&&(a=(t.className.match(r)||[,""])[1]),!a){var o=(s.match(/\.(\w+)$/)||[,""])[1];a=e[o]||o}var l=document.createElement("code");l.className="language-"+a,t.textContent="",l.textContent="Loading…",t.appendChild(l);var i=new XMLHttpRequest;i.open("GET",s,!0),i.onreadystatechange=function(){4==i.readyState&&(i.status<400&&i.responseText?(l.textContent=i.responseText,Prism.highlightElement(l)):l.textContent=i.status>=400?"✖ Error "+i.status+" while fetching file: "+i.statusText:"✖ Error: File does not exist or is empty")},i.send(null)})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight))}(); \ No newline at end of file diff --git a/plugins/show-language/prism-show-language.js b/plugins/show-language/prism-show-language.js index e9248a3d82..e878b08fc8 100644 --- a/plugins/show-language/prism-show-language.js +++ b/plugins/show-language/prism-show-language.js @@ -5,7 +5,7 @@ if (typeof self === 'undefined' || !self.Prism || !self.document) { } // The languages map is built automatically with gulp -var Languages = /*languages_placeholder[*/{"css":"CSS","clike":"C-like","javascript":"JavaScript","abap":"ABAP","actionscript":"ActionScript","apacheconf":"Apache Configuration","apl":"APL","applescript":"AppleScript","asciidoc":"AsciiDoc","aspnet":"ASP.NET (C#)","autoit":"AutoIt","autohotkey":"AutoHotkey","basic":"BASIC","csharp":"C#","cpp":"C++","coffeescript":"CoffeeScript","css-extras":"CSS Extras","fsharp":"F#","glsl":"GLSL","http":"HTTP","inform7":"Inform 7","json":"JSON","latex":"LaTeX","lolcode":"LOLCODE","matlab":"MATLAB","mel":"MEL","nasm":"NASM","nginx":"nginx","nsis":"NSIS","objectivec":"Objective-C","ocaml":"OCaml","parigp":"PARI/GP","php":"PHP","php-extras":"PHP Extras","powershell":"PowerShell","jsx":"React JSX","rest":"reST (reStructuredText)","sas":"SAS","sass":"Sass (Sass)","scss":"Sass (Scss)","sql":"SQL","typescript":"TypeScript","vhdl":"VHDL","vim":"vim","wiki":"Wiki markup","yaml":"YAML"}/*]*/; +var Languages = /*languages_placeholder[*/{"html":"HTML","xml":"XML","svg":"SVG","mathml":"MathML","css":"CSS","clike":"C-like","javascript":"JavaScript","abap":"ABAP","actionscript":"ActionScript","apacheconf":"Apache Configuration","apl":"APL","applescript":"AppleScript","asciidoc":"AsciiDoc","aspnet":"ASP.NET (C#)","autoit":"AutoIt","autohotkey":"AutoHotkey","basic":"BASIC","csharp":"C#","cpp":"C++","coffeescript":"CoffeeScript","css-extras":"CSS Extras","fsharp":"F#","glsl":"GLSL","http":"HTTP","inform7":"Inform 7","json":"JSON","latex":"LaTeX","lolcode":"LOLCODE","matlab":"MATLAB","mel":"MEL","nasm":"NASM","nginx":"nginx","nsis":"NSIS","objectivec":"Objective-C","ocaml":"OCaml","parigp":"PARI/GP","php":"PHP","php-extras":"PHP Extras","powershell":"PowerShell","jsx":"React JSX","rest":"reST (reStructuredText)","sas":"SAS","sass":"Sass (Sass)","scss":"Sass (Scss)","sql":"SQL","typescript":"TypeScript","vhdl":"VHDL","vim":"vim","wiki":"Wiki markup","yaml":"YAML"}/*]*/; Prism.hooks.add('before-highlight', function(env) { var pre = env.element.parentNode; if (!pre || !/pre/i.test(pre.nodeName)) { diff --git a/plugins/show-language/prism-show-language.min.js b/plugins/show-language/prism-show-language.min.js index 7bb5204702..e4f5d15bd1 100644 --- a/plugins/show-language/prism-show-language.min.js +++ b/plugins/show-language/prism-show-language.min.js @@ -1 +1 @@ -!function(){if("undefined"!=typeof self&&self.Prism&&self.document){var e={css:"CSS",clike:"C-like",javascript:"JavaScript",abap:"ABAP",actionscript:"ActionScript",apacheconf:"Apache Configuration",apl:"APL",applescript:"AppleScript",asciidoc:"AsciiDoc",aspnet:"ASP.NET (C#)",autoit:"AutoIt",autohotkey:"AutoHotkey",basic:"BASIC",csharp:"C#",cpp:"C++",coffeescript:"CoffeeScript","css-extras":"CSS Extras",fsharp:"F#",glsl:"GLSL",http:"HTTP",inform7:"Inform 7",json:"JSON",latex:"LaTeX",lolcode:"LOLCODE",matlab:"MATLAB",mel:"MEL",nasm:"NASM",nginx:"nginx",nsis:"NSIS",objectivec:"Objective-C",ocaml:"OCaml",parigp:"PARI/GP",php:"PHP","php-extras":"PHP Extras",powershell:"PowerShell",jsx:"React JSX",rest:"reST (reStructuredText)",sas:"SAS",sass:"Sass (Sass)",scss:"Sass (Scss)",sql:"SQL",typescript:"TypeScript",vhdl:"VHDL",vim:"vim",wiki:"Wiki markup",yaml:"YAML"};Prism.hooks.add("before-highlight",function(s){var a=s.element.parentNode;if(a&&/pre/i.test(a.nodeName)){var t,i,r=a.getAttribute("data-language")||e[s.language]||s.language.substring(0,1).toUpperCase()+s.language.substring(1),l=a.previousSibling;l&&/\s*\bprism-show-language\b\s*/.test(l.className)&&l.firstChild&&/\s*\bprism-show-language-label\b\s*/.test(l.firstChild.className)?i=l.firstChild:(t=document.createElement("div"),i=document.createElement("div"),i.className="prism-show-language-label",t.className="prism-show-language",t.appendChild(i),a.parentNode.insertBefore(t,a)),i.innerHTML=r}})}}(); \ No newline at end of file +!function(){if("undefined"!=typeof self&&self.Prism&&self.document){var e={html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",css:"CSS",clike:"C-like",javascript:"JavaScript",abap:"ABAP",actionscript:"ActionScript",apacheconf:"Apache Configuration",apl:"APL",applescript:"AppleScript",asciidoc:"AsciiDoc",aspnet:"ASP.NET (C#)",autoit:"AutoIt",autohotkey:"AutoHotkey",basic:"BASIC",csharp:"C#",cpp:"C++",coffeescript:"CoffeeScript","css-extras":"CSS Extras",fsharp:"F#",glsl:"GLSL",http:"HTTP",inform7:"Inform 7",json:"JSON",latex:"LaTeX",lolcode:"LOLCODE",matlab:"MATLAB",mel:"MEL",nasm:"NASM",nginx:"nginx",nsis:"NSIS",objectivec:"Objective-C",ocaml:"OCaml",parigp:"PARI/GP",php:"PHP","php-extras":"PHP Extras",powershell:"PowerShell",jsx:"React JSX",rest:"reST (reStructuredText)",sas:"SAS",sass:"Sass (Sass)",scss:"Sass (Scss)",sql:"SQL",typescript:"TypeScript",vhdl:"VHDL",vim:"vim",wiki:"Wiki markup",yaml:"YAML"};Prism.hooks.add("before-highlight",function(s){var a=s.element.parentNode;if(a&&/pre/i.test(a.nodeName)){var t,i,r=a.getAttribute("data-language")||e[s.language]||s.language.substring(0,1).toUpperCase()+s.language.substring(1),l=a.previousSibling;l&&/\s*\bprism-show-language\b\s*/.test(l.className)&&l.firstChild&&/\s*\bprism-show-language-label\b\s*/.test(l.firstChild.className)?i=l.firstChild:(t=document.createElement("div"),i=document.createElement("div"),i.className="prism-show-language-label",t.className="prism-show-language",t.appendChild(i),a.parentNode.insertBefore(t,a)),i.innerHTML=r}})}}(); \ No newline at end of file diff --git a/prism.js b/prism.js index 8474702d94..f296c0d903 100644 --- a/prism.js +++ b/prism.js @@ -655,13 +655,14 @@ Prism.languages.js = Prism.languages.javascript; var Extensions = { 'js': 'javascript', - 'html': 'markup', - 'svg': 'markup', - 'xml': 'markup', 'py': 'python', 'rb': 'ruby', 'ps1': 'powershell', - 'psm1': 'powershell' + 'psm1': 'powershell', + 'sh': 'bash', + 'bat': 'batch', + 'h': 'c', + 'tex': 'latex' }; if(Array.prototype.forEach) { // Check to prevent error in IE8 From 2705c5099376ccfe80625cb6368b69dfbee74307 Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Thu, 24 Sep 2015 19:36:10 +0200 Subject: [PATCH 18/22] Partial solution for the "Comment-like substrings"-problem This patch introduces a new attribute called `greedy`. The attribute is a simple boolean flag. If there is no match for a greedy pattern it can concatenate the next two tokens into a single string and try to match on this string again. If a match is found on the second attempt, then the old tokens are deleted and replaced by the new match. This solves the "Comment-like substrings"-problem for exactly one comment at very little cost. With this patch the following code is highlighted correctly: "foo /* bar */ baz"; "foo // bar"; /lala"test"sdf/; This approach fails if there are more than one comments inside the string: "foo /* bar */ baz /* bar */ baz"; Signed-off-by: Andreas Rohner --- components/prism-clike.js | 5 ++- components/prism-clike.min.js | 2 +- components/prism-core.js | 41 ++++++++++++++++++++++--- components/prism-core.min.js | 2 +- components/prism-javascript.js | 3 +- components/prism-javascript.min.js | 2 +- prism.js | 49 ++++++++++++++++++++++++++---- 7 files changed, 89 insertions(+), 15 deletions(-) diff --git a/components/prism-clike.js b/components/prism-clike.js index 08dccaf304..c691011d34 100644 --- a/components/prism-clike.js +++ b/components/prism-clike.js @@ -9,7 +9,10 @@ Prism.languages.clike = { lookbehind: true } ], - 'string': /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, + 'string': { + pattern: /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, + greedy: true + }, 'class-name': { pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i, lookbehind: true, diff --git a/components/prism-clike.min.js b/components/prism-clike.min.js index dddc16e784..951774c575 100644 --- a/components/prism-clike.min.js +++ b/components/prism-clike.min.js @@ -1 +1 @@ -Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; \ No newline at end of file +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; \ No newline at end of file diff --git a/components/prism-core.js b/components/prism-core.js index 0ca386ceb7..004139690c 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -274,6 +274,7 @@ var _ = _self.Prism = { var pattern = patterns[j], inside = pattern.inside, lookbehind = !!pattern.lookbehind, + greedy = !!pattern.greedy, lookbehindLength = 0, alias = pattern.alias; @@ -294,7 +295,38 @@ var _ = _self.Prism = { pattern.lastIndex = 0; - var match = pattern.exec(str); + var match = pattern.exec(str), + delNum = 1; + + if (!match && greedy && i != strarr.length - 1) { + var nextToken = strarr[i + 1].matchedStr || strarr[i + 1], + combStr = str + nextToken; + + if (i < strarr.length - 2) { + combStr += strarr[i + 2].matchedStr || strarr[i + 2]; + } + + pattern.lastIndex = 0; + match = pattern.exec(combStr); + if (!match) { + continue; + } + + var from = match.index + (lookbehind ? match[1].length : 0); + if (from >= str.length) { + continue; + } + var to = match.index + match[0].length, + len = str.length + nextToken.length; + + delNum = 3; + + if (to <= len) { + delNum = 2; + combStr = combStr.slice(0, len); + } + str = combStr; + } if (match) { if(lookbehind) { @@ -308,13 +340,13 @@ var _ = _self.Prism = { before = str.slice(0, from + 1), after = str.slice(to + 1); - var args = [i, 1]; + var args = [i, delNum]; if (before) { args.push(before); } - var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias); + var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match); args.push(wrapped); @@ -356,10 +388,11 @@ var _ = _self.Prism = { } }; -var Token = _.Token = function(type, content, alias) { +var Token = _.Token = function(type, content, alias, matchedStr) { this.type = type; this.content = content; this.alias = alias; + this.matchedStr = matchedStr || null; }; Token.stringify = function(o, language, parent) { diff --git a/components/prism-core.min.js b/components/prism-core.min.js index 577b2d0c74..391f6ec25f 100644 --- a/components/prism-core.min.js +++ b/components/prism-core.min.js @@ -1 +1 @@ -var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){g&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,b=y+v,k=d.slice(0,y+1),w=d.slice(b+1),_=[p,1];k&&_.push(k);var P=new a(i,c?n.tokenize(m,c):m,h);_.push(P),w&&_.push(w),Array.prototype.splice.apply(r,_)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(m instanceof a)){u.lastIndex=0;var v=u.exec(m),y=1;if(!v&&h&&p!=r.length-1){var b=r[p+1].matchedStr||r[p+1],k=m+b;if(p=m.length)continue;var _=v.index+v[0].length,P=m.length+b.length;y=3,P>=_&&(y=2,k=k.slice(0,P)),m=k}if(v){g&&(f=v[1].length);var w=v.index-1+f,v=v[0].slice(f),P=v.length,_=w+P,S=m.slice(0,w+1),O=m.slice(_+1),j=[p,y];S&&j.push(S);var A=new a(i,c?n.tokenize(v,c):v,d,v);j.push(A),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n,a){this.type=e,this.content=t,this.alias=n,this.matchedStr=a||null};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file diff --git a/components/prism-javascript.js b/components/prism-javascript.js index 27663970c7..a4202878b3 100644 --- a/components/prism-javascript.js +++ b/components/prism-javascript.js @@ -8,7 +8,8 @@ Prism.languages.javascript = Prism.languages.extend('clike', { Prism.languages.insertBefore('javascript', 'keyword', { 'regex': { pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/, - lookbehind: true + lookbehind: true, + greedy: true } }); diff --git a/components/prism-javascript.min.js b/components/prism-javascript.min.js index 6bd00079ac..14808c8e51 100644 --- a/components/prism-javascript.min.js +++ b/components/prism-javascript.min.js @@ -1 +1 @@ -Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\\\|\\?[^\\])*?`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; \ No newline at end of file +Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0,greedy:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\\\|\\?[^\\])*?`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; \ No newline at end of file diff --git a/prism.js b/prism.js index f296c0d903..a2b5bd01c8 100644 --- a/prism.js +++ b/prism.js @@ -279,6 +279,7 @@ var _ = _self.Prism = { var pattern = patterns[j], inside = pattern.inside, lookbehind = !!pattern.lookbehind, + greedy = !!pattern.greedy, lookbehindLength = 0, alias = pattern.alias; @@ -299,7 +300,38 @@ var _ = _self.Prism = { pattern.lastIndex = 0; - var match = pattern.exec(str); + var match = pattern.exec(str), + delNum = 1; + + if (!match && greedy && i != strarr.length - 1) { + var nextToken = strarr[i + 1].matchedStr || strarr[i + 1], + combStr = str + nextToken; + + if (i < strarr.length - 2) { + combStr += strarr[i + 2].matchedStr || strarr[i + 2]; + } + + pattern.lastIndex = 0; + match = pattern.exec(combStr); + if (!match) { + continue; + } + + var from = match.index + (lookbehind ? match[1].length : 0); + if (from >= str.length) { + continue; + } + var to = match.index + match[0].length, + len = str.length + nextToken.length; + + delNum = 3; + + if (to <= len) { + delNum = 2; + combStr = combStr.slice(0, len); + } + str = combStr; + } if (match) { if(lookbehind) { @@ -313,13 +345,13 @@ var _ = _self.Prism = { before = str.slice(0, from + 1), after = str.slice(to + 1); - var args = [i, 1]; + var args = [i, delNum]; if (before) { args.push(before); } - var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias); + var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match); args.push(wrapped); @@ -361,10 +393,11 @@ var _ = _self.Prism = { } }; -var Token = _.Token = function(type, content, alias) { +var Token = _.Token = function(type, content, alias, matchedStr) { this.type = type; this.content = content; this.alias = alias; + this.matchedStr = matchedStr || null; }; Token.stringify = function(o, language, parent) { @@ -575,7 +608,10 @@ Prism.languages.clike = { lookbehind: true } ], - 'string': /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, + 'string': { + pattern: /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, + greedy: true + }, 'class-name': { pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i, lookbehind: true, @@ -606,7 +642,8 @@ Prism.languages.javascript = Prism.languages.extend('clike', { Prism.languages.insertBefore('javascript', 'keyword', { 'regex': { pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/, - lookbehind: true + lookbehind: true, + greedy: true } }); From 90432f96cf48277b75411a1812957ce1e8a3c42f Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Thu, 18 Feb 2016 01:22:40 +0100 Subject: [PATCH 19/22] Add comments to better document the greedy-pattern feature --- components/prism-core.js | 9 +++++++++ prism.js | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/components/prism-core.js b/components/prism-core.js index 004139690c..73267dc87b 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -298,7 +298,10 @@ var _ = _self.Prism = { var match = pattern.exec(str), delNum = 1; + // Greedy patterns can override/remove up to two previously + // matched tokens if (!match && greedy && i != strarr.length - 1) { + // Reconstruct the original text using the next two tokens var nextToken = strarr[i + 1].matchedStr || strarr[i + 1], combStr = str + nextToken; @@ -306,6 +309,7 @@ var _ = _self.Prism = { combStr += strarr[i + 2].matchedStr || strarr[i + 2]; } + // Try the pattern again on the reconstructed text pattern.lastIndex = 0; match = pattern.exec(combStr); if (!match) { @@ -313,12 +317,16 @@ var _ = _self.Prism = { } var from = match.index + (lookbehind ? match[1].length : 0); + // To be a valid candidate, the new match has to start + // inside of str if (from >= str.length) { continue; } var to = match.index + match[0].length, len = str.length + nextToken.length; + // Number of tokens to delete and replace with the new + // match delNum = 3; if (to <= len) { @@ -392,6 +400,7 @@ var Token = _.Token = function(type, content, alias, matchedStr) { this.type = type; this.content = content; this.alias = alias; + // Copy of the full string this token was created from this.matchedStr = matchedStr || null; }; diff --git a/prism.js b/prism.js index a2b5bd01c8..20b0e0fa8b 100644 --- a/prism.js +++ b/prism.js @@ -303,7 +303,10 @@ var _ = _self.Prism = { var match = pattern.exec(str), delNum = 1; + // Greedy patterns can override/remove up to two previously + // matched tokens if (!match && greedy && i != strarr.length - 1) { + // Reconstruct the original text using the next two tokens var nextToken = strarr[i + 1].matchedStr || strarr[i + 1], combStr = str + nextToken; @@ -311,6 +314,7 @@ var _ = _self.Prism = { combStr += strarr[i + 2].matchedStr || strarr[i + 2]; } + // Try the pattern again on the reconstructed text pattern.lastIndex = 0; match = pattern.exec(combStr); if (!match) { @@ -318,12 +322,16 @@ var _ = _self.Prism = { } var from = match.index + (lookbehind ? match[1].length : 0); + // To be a valid candidate, the new match has to start + // inside of str if (from >= str.length) { continue; } var to = match.index + match[0].length, len = str.length + nextToken.length; + // Number of tokens to delete and replace with the new + // match delNum = 3; if (to <= len) { @@ -397,6 +405,7 @@ var Token = _.Token = function(type, content, alias, matchedStr) { this.type = type; this.content = content; this.alias = alias; + // Copy of the full string this token was created from this.matchedStr = matchedStr || null; }; From 24a0936138b4820d09eb1f3311c18b7cf462a270 Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Fri, 19 Feb 2016 13:38:54 +0100 Subject: [PATCH 20/22] Fix double HTML-encoding bug in Groovy language --- components/prism-groovy.js | 4 ++++ components/prism-groovy.min.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/prism-groovy.js b/components/prism-groovy.js index dfb3657390..599ad406f4 100644 --- a/components/prism-groovy.js +++ b/components/prism-groovy.js @@ -37,6 +37,10 @@ Prism.hooks.add('wrap', function(env) { if (delimiter === '$') { pattern = /([^\$])(\$(\{.*?\}|[\w\.]+))/; } + + // To prevent double HTML-ecoding we have to decode env.content first + env.content = env.content.replace(/&/g, '&').replace(/</g, '<'); + env.content = Prism.highlight(env.content, { 'expression': { pattern: pattern, diff --git a/components/prism-groovy.min.js b/components/prism-groovy.min.js index 6069cf5c71..23fd0d5167 100644 --- a/components/prism-groovy.min.js +++ b/components/prism-groovy.min.js @@ -1 +1 @@ -Prism.languages.groovy=Prism.languages.extend("clike",{keyword:/\b(as|def|in|abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|trait|transient|try|void|volatile|while)\b/,string:/("""|''')[\W\w]*?\1|("|'|\/)(?:\\?.)*?\2|(\$\/)(\$\/\$|[\W\w])*?\/\$/,number:/\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?[\d]+)?)[glidf]?\b/i,operator:{pattern:/(^|[^.])(~|==?~?|\?[.:]?|\*(?:[.=]|\*=?)?|\.[@&]|\.\.<|\.{1,2}(?!\.)|-[-=>]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),Prism.languages.insertBefore("groovy","string",{shebang:{pattern:/#!.+/,alias:"comment"}}),Prism.languages.insertBefore("groovy","punctuation",{"spock-block":/\b(setup|given|when|then|and|cleanup|expect|where):/}),Prism.languages.insertBefore("groovy","function",{annotation:{pattern:/(^|[^.])@\w+/,lookbehind:!0}}),Prism.hooks.add("wrap",function(e){if("groovy"===e.language&&"string"===e.type){var t=e.content[0];if("'"!=t){var n=/([^\\])(\$(\{.*?\}|[\w\.]+))/;"$"===t&&(n=/([^\$])(\$(\{.*?\}|[\w\.]+))/),e.content=Prism.highlight(e.content,{expression:{pattern:n,lookbehind:!0,inside:Prism.languages.groovy}}),e.classes.push("/"===t?"regex":"gstring")}}}); \ No newline at end of file +Prism.languages.groovy=Prism.languages.extend("clike",{keyword:/\b(as|def|in|abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|trait|transient|try|void|volatile|while)\b/,string:/("""|''')[\W\w]*?\1|("|'|\/)(?:\\?.)*?\2|(\$\/)(\$\/\$|[\W\w])*?\/\$/,number:/\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?[\d]+)?)[glidf]?\b/i,operator:{pattern:/(^|[^.])(~|==?~?|\?[.:]?|\*(?:[.=]|\*=?)?|\.[@&]|\.\.<|\.{1,2}(?!\.)|-[-=>]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),Prism.languages.insertBefore("groovy","string",{shebang:{pattern:/#!.+/,alias:"comment"}}),Prism.languages.insertBefore("groovy","punctuation",{"spock-block":/\b(setup|given|when|then|and|cleanup|expect|where):/}),Prism.languages.insertBefore("groovy","function",{annotation:{pattern:/(^|[^.])@\w+/,lookbehind:!0}}),Prism.hooks.add("wrap",function(e){if("groovy"===e.language&&"string"===e.type){var t=e.content[0];if("'"!=t){var n=/([^\\])(\$(\{.*?\}|[\w\.]+))/;"$"===t&&(n=/([^\$])(\$(\{.*?\}|[\w\.]+))/),e.content=e.content.replace(/&/g,"&").replace(/</g,"<"),e.content=Prism.highlight(e.content,{expression:{pattern:n,lookbehind:!0,inside:Prism.languages.groovy}}),e.classes.push("/"===t?"regex":"gstring")}}}); \ No newline at end of file From c88036ca4a935bcbec48fb8ff9a9cbf0d2a5889d Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Fri, 19 Feb 2016 22:27:22 +0100 Subject: [PATCH 21/22] Add tests for new greedy-pattern feature and fix bug in Kotlin This patch adds tests for the new greedy-pattern feature and fixes a small bug in the Kotlin language, that resulted from that. It also cleans up some of the comments and refactors a few lines in the tokenloop. --- components/prism-core.js | 50 +++++++++---------- components/prism-core.min.js | 2 +- components/prism-kotlin.js | 8 +-- components/prism-kotlin.min.js | 2 +- prism.js | 50 +++++++++---------- tests/languages/clike/string_feature.test | 6 ++- tests/languages/javascript/regex_feature.test | 6 ++- 7 files changed, 61 insertions(+), 63 deletions(-) diff --git a/components/prism-core.js b/components/prism-core.js index 73267dc87b..6ba78c74f9 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -298,8 +298,7 @@ var _ = _self.Prism = { var match = pattern.exec(str), delNum = 1; - // Greedy patterns can override/remove up to two previously - // matched tokens + // Greedy patterns can override/remove up to two previously matched tokens if (!match && greedy && i != strarr.length - 1) { // Reconstruct the original text using the next two tokens var nextToken = strarr[i + 1].matchedStr || strarr[i + 1], @@ -317,16 +316,14 @@ var _ = _self.Prism = { } var from = match.index + (lookbehind ? match[1].length : 0); - // To be a valid candidate, the new match has to start - // inside of str + // To be a valid candidate, the new match has to start inside of str if (from >= str.length) { continue; } var to = match.index + match[0].length, len = str.length + nextToken.length; - // Number of tokens to delete and replace with the new - // match + // Number of tokens to delete and replace with the new match delNum = 3; if (to <= len) { @@ -336,34 +333,35 @@ var _ = _self.Prism = { str = combStr; } - if (match) { - if(lookbehind) { - lookbehindLength = match[1].length; - } + if (!match) { + continue; + } - var from = match.index - 1 + lookbehindLength, - match = match[0].slice(lookbehindLength), - len = match.length, - to = from + len, - before = str.slice(0, from + 1), - after = str.slice(to + 1); + if(lookbehind) { + lookbehindLength = match[1].length; + } - var args = [i, delNum]; + var from = match.index + lookbehindLength, + match = match[0].slice(lookbehindLength), + to = from + match.length, + before = str.slice(0, from), + after = str.slice(to); - if (before) { - args.push(before); - } + var args = [i, delNum]; - var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match); + if (before) { + args.push(before); + } - args.push(wrapped); + var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match); - if (after) { - args.push(after); - } + args.push(wrapped); - Array.prototype.splice.apply(strarr, args); + if (after) { + args.push(after); } + + Array.prototype.splice.apply(strarr, args); } } } diff --git a/components/prism-core.min.js b/components/prism-core.min.js index 391f6ec25f..5ccafc93c6 100644 --- a/components/prism-core.min.js +++ b/components/prism-core.min.js @@ -1 +1 @@ -var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(m instanceof a)){u.lastIndex=0;var v=u.exec(m),y=1;if(!v&&h&&p!=r.length-1){var b=r[p+1].matchedStr||r[p+1],k=m+b;if(p=m.length)continue;var _=v.index+v[0].length,P=m.length+b.length;y=3,P>=_&&(y=2,k=k.slice(0,P)),m=k}if(v){g&&(f=v[1].length);var w=v.index-1+f,v=v[0].slice(f),P=v.length,_=w+P,S=m.slice(0,w+1),O=m.slice(_+1),j=[p,y];S&&j.push(S);var A=new a(i,c?n.tokenize(v,c):v,d,v);j.push(A),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n,a){this.type=e,this.content=t,this.alias=n,this.matchedStr=a||null};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(m instanceof a)){u.lastIndex=0;var v=u.exec(m),y=1;if(!v&&h&&p!=r.length-1){var b=r[p+1].matchedStr||r[p+1],k=m+b;if(p=m.length)continue;var _=v.index+v[0].length,P=m.length+b.length;y=3,P>=_&&(y=2,k=k.slice(0,P)),m=k}if(v){g&&(f=v[1].length);var w=v.index+f,v=v[0].slice(f),_=w+v.length,S=m.slice(0,w),O=m.slice(_),j=[p,y];S&&j.push(S);var A=new a(i,c?n.tokenize(v,c):v,d,v);j.push(A),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n,a){this.type=e,this.content=t,this.alias=n,this.matchedStr=a||null};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file diff --git a/components/prism-kotlin.js b/components/prism-kotlin.js index e473c2fbf8..2c11f898a3 100644 --- a/components/prism-kotlin.js +++ b/components/prism-kotlin.js @@ -55,13 +55,7 @@ } ]; - Prism.languages.kotlin['string'] = { - pattern: Prism.languages.kotlin['string'], - inside: { - interpolation: interpolation - } - }; - Prism.languages.kotlin['raw-string'].inside = { + Prism.languages.kotlin['string'].inside = Prism.languages.kotlin['raw-string'].inside = { interpolation: interpolation }; diff --git a/components/prism-kotlin.min.js b/components/prism-kotlin.min.js index dce90b6446..f634489cde 100644 --- a/components/prism-kotlin.min.js +++ b/components/prism-kotlin.min.js @@ -1 +1 @@ -!function(n){n.languages.kotlin=n.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|else|enum|final|finally|for|fun|get|if|import|in|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|out|override|package|private|protected|public|reified|return|sealed|set|super|tailrec|this|throw|to|try|val|var|when|where|while)\b/,lookbehind:!0},"function":[/\w+(?=\s*\()/,{pattern:/(\.)\w+(?=\s*\{)/,lookbehind:!0}],number:/\b(?:0[bx][\da-fA-F]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete n.languages.kotlin["class-name"],n.languages.insertBefore("kotlin","string",{"raw-string":{pattern:/(["'])\1\1[\s\S]*?\1{3}/,alias:"string"}}),n.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),n.languages.insertBefore("kotlin","function",{label:{pattern:/\w+@|@\w+/,alias:"symbol"}});var e=[{pattern:/\$\{[^}]+\}/,inside:{delimiter:{pattern:/^\$\{|\}$/,alias:"variable"},rest:n.util.clone(n.languages.kotlin)}},{pattern:/\$\w+/,alias:"variable"}];n.languages.kotlin.string={pattern:n.languages.kotlin.string,inside:{interpolation:e}},n.languages.kotlin["raw-string"].inside={interpolation:e}}(Prism); \ No newline at end of file +!function(n){n.languages.kotlin=n.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|else|enum|final|finally|for|fun|get|if|import|in|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|out|override|package|private|protected|public|reified|return|sealed|set|super|tailrec|this|throw|to|try|val|var|when|where|while)\b/,lookbehind:!0},"function":[/\w+(?=\s*\()/,{pattern:/(\.)\w+(?=\s*\{)/,lookbehind:!0}],number:/\b(?:0[bx][\da-fA-F]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete n.languages.kotlin["class-name"],n.languages.insertBefore("kotlin","string",{"raw-string":{pattern:/(["'])\1\1[\s\S]*?\1{3}/,alias:"string"}}),n.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),n.languages.insertBefore("kotlin","function",{label:{pattern:/\w+@|@\w+/,alias:"symbol"}});var e=[{pattern:/\$\{[^}]+\}/,inside:{delimiter:{pattern:/^\$\{|\}$/,alias:"variable"},rest:n.util.clone(n.languages.kotlin)}},{pattern:/\$\w+/,alias:"variable"}];n.languages.kotlin.string.inside=n.languages.kotlin["raw-string"].inside={interpolation:e}}(Prism); \ No newline at end of file diff --git a/prism.js b/prism.js index 20b0e0fa8b..cb29a8c69e 100644 --- a/prism.js +++ b/prism.js @@ -303,8 +303,7 @@ var _ = _self.Prism = { var match = pattern.exec(str), delNum = 1; - // Greedy patterns can override/remove up to two previously - // matched tokens + // Greedy patterns can override/remove up to two previously matched tokens if (!match && greedy && i != strarr.length - 1) { // Reconstruct the original text using the next two tokens var nextToken = strarr[i + 1].matchedStr || strarr[i + 1], @@ -322,16 +321,14 @@ var _ = _self.Prism = { } var from = match.index + (lookbehind ? match[1].length : 0); - // To be a valid candidate, the new match has to start - // inside of str + // To be a valid candidate, the new match has to start inside of str if (from >= str.length) { continue; } var to = match.index + match[0].length, len = str.length + nextToken.length; - // Number of tokens to delete and replace with the new - // match + // Number of tokens to delete and replace with the new match delNum = 3; if (to <= len) { @@ -341,34 +338,35 @@ var _ = _self.Prism = { str = combStr; } - if (match) { - if(lookbehind) { - lookbehindLength = match[1].length; - } + if (!match) { + continue; + } - var from = match.index - 1 + lookbehindLength, - match = match[0].slice(lookbehindLength), - len = match.length, - to = from + len, - before = str.slice(0, from + 1), - after = str.slice(to + 1); + if(lookbehind) { + lookbehindLength = match[1].length; + } - var args = [i, delNum]; + var from = match.index + lookbehindLength, + match = match[0].slice(lookbehindLength), + to = from + match.length, + before = str.slice(0, from), + after = str.slice(to); - if (before) { - args.push(before); - } + var args = [i, delNum]; - var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match); + if (before) { + args.push(before); + } - args.push(wrapped); + var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match); - if (after) { - args.push(after); - } + args.push(wrapped); - Array.prototype.splice.apply(strarr, args); + if (after) { + args.push(after); } + + Array.prototype.splice.apply(strarr, args); } } } diff --git a/tests/languages/clike/string_feature.test b/tests/languages/clike/string_feature.test index 0daabede0d..983ca42f64 100644 --- a/tests/languages/clike/string_feature.test +++ b/tests/languages/clike/string_feature.test @@ -6,6 +6,8 @@ bar" 'foo\ bar' +"foo /* comment */ bar" +'foo // bar' ---------------------------------------------------- @@ -15,7 +17,9 @@ bar' ["string", "\"f\\\"oo\""], ["string", "'b\\'ar'"], ["string", "\"foo\\\r\nbar\""], - ["string", "'foo\\\r\nbar'"] + ["string", "'foo\\\r\nbar'"], + ["string", "\"foo /* comment */ bar\""], + ["string", "'foo // bar'"] ] ---------------------------------------------------- diff --git a/tests/languages/javascript/regex_feature.test b/tests/languages/javascript/regex_feature.test index a1268d40fb..e2ed460a7c 100644 --- a/tests/languages/javascript/regex_feature.test +++ b/tests/languages/javascript/regex_feature.test @@ -1,13 +1,17 @@ /foo bar/ /foo/gimyu, /[\[\]]{2,4}(?:foo)*/; +/foo"test"bar/ +/foo\// ---------------------------------------------------- [ ["regex", "/foo bar/"], ["regex", "/foo/gimyu"], ["punctuation", ","], - ["regex", "/[\\[\\]]{2,4}(?:foo)*/"], ["punctuation", ";"] + ["regex", "/[\\[\\]]{2,4}(?:foo)*/"], ["punctuation", ";"], + ["regex", "/foo\"test\"bar/"], + ["regex", "/foo\\//"] ] ---------------------------------------------------- From 16f6f39fac84cf31742e852f3696cb4f07c57d86 Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Fri, 19 Feb 2016 23:11:10 +0100 Subject: [PATCH 22/22] Remove direction-property from themes Remove direction-property from all themes, because it is incompatible with the EPUB-format. This was reported in issue #902. --- themes/prism-coy.css | 1 - themes/prism-dark.css | 1 - themes/prism-funky.css | 1 - themes/prism-okaidia.css | 1 - themes/prism-solarizedlight.css | 1 - themes/prism-tomorrow.css | 1 - themes/prism-twilight.css | 1 - themes/prism.css | 1 - 8 files changed, 8 deletions(-) diff --git a/themes/prism-coy.css b/themes/prism-coy.css index 147b59b9b6..cc871d364e 100644 --- a/themes/prism-coy.css +++ b/themes/prism-coy.css @@ -9,7 +9,6 @@ pre[class*="language-"] { color: black; background: none; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - direction: ltr; text-align: left; white-space: pre; word-spacing: normal; diff --git a/themes/prism-dark.css b/themes/prism-dark.css index 8d5c291561..7f4572877b 100644 --- a/themes/prism-dark.css +++ b/themes/prism-dark.css @@ -10,7 +10,6 @@ pre[class*="language-"] { background: none; text-shadow: 0 -.1em .2em black; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - direction: ltr; text-align: left; white-space: pre; word-spacing: normal; diff --git a/themes/prism-funky.css b/themes/prism-funky.css index e4d676d6ca..dd3be06188 100644 --- a/themes/prism-funky.css +++ b/themes/prism-funky.css @@ -7,7 +7,6 @@ code[class*="language-"], pre[class*="language-"] { font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - direction: ltr; text-align: left; white-space: pre; word-spacing: normal; diff --git a/themes/prism-okaidia.css b/themes/prism-okaidia.css index d7c6bb7d53..3e0da3575f 100644 --- a/themes/prism-okaidia.css +++ b/themes/prism-okaidia.css @@ -10,7 +10,6 @@ pre[class*="language-"] { background: none; text-shadow: 0 1px rgba(0, 0, 0, 0.3); font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - direction: ltr; text-align: left; white-space: pre; word-spacing: normal; diff --git a/themes/prism-solarizedlight.css b/themes/prism-solarizedlight.css index 4418410433..bad770395b 100644 --- a/themes/prism-solarizedlight.css +++ b/themes/prism-solarizedlight.css @@ -32,7 +32,6 @@ code[class*="language-"], pre[class*="language-"] { color: #657b83; /* base00 */ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - direction: ltr; text-align: left; white-space: pre; word-spacing: normal; diff --git a/themes/prism-tomorrow.css b/themes/prism-tomorrow.css index 5c192741d9..6add2d2620 100644 --- a/themes/prism-tomorrow.css +++ b/themes/prism-tomorrow.css @@ -9,7 +9,6 @@ pre[class*="language-"] { color: #ccc; background: none; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - direction: ltr; text-align: left; white-space: pre; word-spacing: normal; diff --git a/themes/prism-twilight.css b/themes/prism-twilight.css index f70e5c50d8..1d819466b9 100644 --- a/themes/prism-twilight.css +++ b/themes/prism-twilight.css @@ -7,7 +7,6 @@ code[class*="language-"], pre[class*="language-"] { color: white; background: none; - direction: ltr; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; text-align: left; text-shadow: 0 -.1em .2em black; diff --git a/themes/prism.css b/themes/prism.css index f677cdf81a..83ef295f42 100644 --- a/themes/prism.css +++ b/themes/prism.css @@ -10,7 +10,6 @@ pre[class*="language-"] { background: none; text-shadow: 0 1px white; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - direction: ltr; text-align: left; white-space: pre; word-spacing: normal;