From c07983eef760f4d8431c5b3cbb36b26fadc772e4 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Wed, 22 Aug 2018 22:01:42 +0200 Subject: [PATCH 01/15] Added pattern builder --- components/prism-core.js | 26 ++++++++++++++++ components/prism-core.min.js | 2 +- components/prism-haml.js | 6 ++-- components/prism-haml.min.js | 2 +- components/prism-pug.js | 4 +-- components/prism-pug.min.js | 2 +- components/prism-pure.js | 6 ++-- components/prism-pure.min.js | 2 +- prism.js | 26 ++++++++++++++++ tests/helper/prism-core-tester.js | 52 +++++++++++++++++++++++++++++++ tests/helper/prism-loader.js | 7 ++++- 11 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 tests/helper/prism-core-tester.js diff --git a/components/prism-core.js b/components/prism-core.js index 0ff3bbf38c..33c718f17e 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -454,6 +454,32 @@ var _ = _self.Prism = { callback(env); } } + }, + + patterns: { + + /** + * asd + * @param {string|RegExp} basePattern + * @param {Object.} replacements + * @returns {RegExp} + */ + build: function build(basePattern, replacements) { + var placeholder = /<<([\w-]+)>>/g; + var source = basePattern.source || basePattern; + var flags = basePattern.flags || (basePattern.exec ? basePattern.toString().match(/[igmuy]*$/)[0] : ''); + + if (build.test) + build.test(basePattern, replacements, placeholder, source, flags); + + source = source.replace(placeholder, function (m, name) { + var replacement = replacements[name]; + return '(?:' + (replacement.source || replacement) + ')'; + }); + + return RegExp(source, flags); + } + } }; diff --git a/components/prism-core.min.js b/components/prism-core.min.js index 1256177db7..0fd946ef77 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={manual:_self.Prism&&_self.Prism.manual,disableWorkerMessageHandler:_self.Prism&&_self.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof r?new r(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)return;if(!(w instanceof s)){if(m&&b!=t.length-1){h.lastIndex=k;var _=h.exec(e);if(!_)break;for(var j=_.index+(d?_[1].length:0),P=_.index+_[0].length,A=b,x=k,O=t.length;O>A&&(P>x||!t[A].type&&!t[A-1].greedy);++A)x+=t[A].length,j>=x&&(++b,k=x);if(t[b]instanceof s)continue;I=A-b,w=e.slice(k,x),_.index-=k}else{h.lastIndex=0;var _=h.exec(w),I=1}if(_){d&&(p=_[1]?_[1].length:0);var j=_.index+p,_=_[0].slice(p),P=j+_.length,N=w.slice(0,j),S=w.slice(P),C=[b,I];N&&(++b,k+=N.length,C.push(N));var E=new s(u,f?n.tokenize(_,f):_,y,_,m);if(C.push(E),S&&C.push(S),Array.prototype.splice.apply(t,C),1!=I&&n.matchGrammar(e,t,r,b,k,!0,u),i)break}else if(i)break}}}}},tokenize:function(e,t){var r=[e],a=t.rest;if(a){for(var l in a)t[l]=a[l];delete t.rest}return n.matchGrammar(e,r,t,0,0,!1),r},hooks:{all:{},add:function(e,t){var r=n.hooks.all;r[e]=r[e]||[],r[e].push(t)},run:function(e,t){var r=n.hooks.all[e];if(r&&r.length)for(var a,l=0;a=r[l++];)a(t)}}},r=n.Token=function(e,t,n,r,a){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!a};if(r.stringify=function(e,t,a){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return r.stringify(n,t,e)}).join("");var l={type:e.type,content:r.stringify(e.content,t,a),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:a};if(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=Object.keys(l.attributes).map(function(e){return e+'="'+(l.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+l.tag+' class="'+l.classes.join(" ")+'"'+(o?" "+o:"")+">"+l.content+""},!_self.document)return _self.addEventListener?(n.disableWorkerMessageHandler||_self.addEventListener("message",function(e){var t=JSON.parse(e.data),r=t.language,a=t.code,l=t.immediateClose;_self.postMessage(n.highlight(a,n.languages[r],r)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return a&&(n.filename=a.src,n.manual||a.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):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={manual:_self.Prism&&_self.Prism.manual,disableWorkerMessageHandler:_self.Prism&&_self.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof r?new r(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)return;if(!(w instanceof s)){if(m&&b!=t.length-1){h.lastIndex=k;var _=h.exec(e);if(!_)break;for(var j=_.index+(d?_[1].length:0),P=_.index+_[0].length,A=b,x=k,O=t.length;O>A&&(P>x||!t[A].type&&!t[A-1].greedy);++A)x+=t[A].length,j>=x&&(++b,k=x);if(t[b]instanceof s)continue;S=A-b,w=e.slice(k,x),_.index-=k}else{h.lastIndex=0;var _=h.exec(w),S=1}if(_){d&&(p=_[1]?_[1].length:0);var j=_.index+p,_=_[0].slice(p),P=j+_.length,I=w.slice(0,j),N=w.slice(P),C=[b,S];I&&(++b,k+=I.length,C.push(I));var E=new s(u,f?n.tokenize(_,f):_,y,_,m);if(C.push(E),N&&C.push(N),Array.prototype.splice.apply(t,C),1!=S&&n.matchGrammar(e,t,r,b,k,!0,u),l)break}else if(l)break}}}}},tokenize:function(e,t){var r=[e],a=t.rest;if(a){for(var i in a)t[i]=a[i];delete t.rest}return n.matchGrammar(e,r,t,0,0,!1),r},hooks:{all:{},add:function(e,t){var r=n.hooks.all;r[e]=r[e]||[],r[e].push(t)},run:function(e,t){var r=n.hooks.all[e];if(r&&r.length)for(var a,i=0;a=r[i++];)a(t)}},patterns:{build:function i(e,t){var n=/<<([\w-]+)>>/g,r=e.source||e,a=e.flags||(e.exec?e.toString().match(/[igmuy]*$/)[0]:"");return i.test&&i.test(e,t,n,r,a),r=r.replace(n,function(e,n){var r=t[n];return"(?:"+(r.source||r)+")"}),RegExp(r,a)}}},r=n.Token=function(e,t,n,r,a){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!a};if(r.stringify=function(e,t,a){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return r.stringify(n,t,e)}).join("");var i={type:e.type,content:r.stringify(e.content,t,a),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:a};if(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=Object.keys(i.attributes).map(function(e){return e+'="'+(i.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""},!_self.document)return _self.addEventListener?(n.disableWorkerMessageHandler||_self.addEventListener("message",function(e){var t=JSON.parse(e.data),r=t.language,a=t.code,i=t.immediateClose;_self.postMessage(n.highlight(a,n.languages[r],r)),i&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return a&&(n.filename=a.src,n.manual||a.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):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-haml.js b/components/prism-haml.js index 4ad53c8879..317e9473da 100644 --- a/components/prism-haml.js +++ b/components/prism-haml.js @@ -116,7 +116,7 @@ } }; - var filter_pattern = '((?:^|\\r?\\n|\\r)([\\t ]*)):{{filter_name}}(?:(?:\\r?\\n|\\r)(?:\\2[\\t ]+.+|\\s*?(?=\\r?\\n|\\r)))+'; + var filter_pattern = /((?:^|\r?\n|\r)([\t ]*)):<>(?:(?:\r?\n|\r)(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/.source; // Non exhaustive list of available filters and associated languages var filters = [ @@ -136,7 +136,7 @@ filter = typeof filter === 'string' ? {filter: filter, language: filter} : filter; if (Prism.languages[filter.language]) { all_filters['filter-' + filter.filter] = { - pattern: RegExp(filter_pattern.replace('{{filter_name}}', filter.filter)), + pattern: Prism.patterns.build(filter_pattern, filter), lookbehind: true, inside: { 'filter-name': { @@ -151,4 +151,4 @@ Prism.languages.insertBefore('haml', 'filter', all_filters); -}(Prism)); \ No newline at end of file +}(Prism)); diff --git a/components/prism-haml.min.js b/components/prism-haml.min.js index 86583e3743..32cf2fef5b 100644 --- a/components/prism-haml.min.js +++ b/components/prism-haml.min.js @@ -1 +1 @@ -!function(e){e.languages.haml={"multiline-comment":{pattern:/((?:^|\r?\n|\r)([\t ]*))(?:\/|-#).*(?:(?:\r?\n|\r)\2[\t ]+.+)*/,lookbehind:!0,alias:"comment"},"multiline-code":[{pattern:/((?:^|\r?\n|\r)([\t ]*)(?:[~-]|[&!]?=)).*,[\t ]*(?:(?:\r?\n|\r)\2[\t ]+.*,[\t ]*)*(?:(?:\r?\n|\r)\2[\t ]+.+)/,lookbehind:!0,inside:{rest:e.languages.ruby}},{pattern:/((?:^|\r?\n|\r)([\t ]*)(?:[~-]|[&!]?=)).*\|[\t ]*(?:(?:\r?\n|\r)\2[\t ]+.*\|[\t ]*)*/,lookbehind:!0,inside:{rest:e.languages.ruby}}],filter:{pattern:/((?:^|\r?\n|\r)([\t ]*)):[\w-]+(?:(?:\r?\n|\r)(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/,lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"}}},markup:{pattern:/((?:^|\r?\n|\r)[\t ]*)<.+/,lookbehind:!0,inside:{rest:e.languages.markup}},doctype:{pattern:/((?:^|\r?\n|\r)[\t ]*)!!!(?: .+)?/,lookbehind:!0},tag:{pattern:/((?:^|\r?\n|\r)[\t ]*)[%.#][\w\-#.]*[\w\-](?:\([^)]+\)|\{(?:\{[^}]+\}|[^}])+\}|\[[^\]]+\])*[\/<>]*/,lookbehind:!0,inside:{attributes:[{pattern:/(^|[^#])\{(?:\{[^}]+\}|[^}])+\}/,lookbehind:!0,inside:{rest:e.languages.ruby}},{pattern:/\([^)]+\)/,inside:{"attr-value":{pattern:/(=\s*)(?:"(?:\\.|[^\\"\r\n])*"|[^)\s]+)/,lookbehind:!0},"attr-name":/[\w:-]+(?=\s*!?=|\s*[,)])/,punctuation:/[=(),]/}},{pattern:/\[[^\]]+\]/,inside:{rest:e.languages.ruby}}],punctuation:/[<>]/}},code:{pattern:/((?:^|\r?\n|\r)[\t ]*(?:[~-]|[&!]?=)).+/,lookbehind:!0,inside:{rest:e.languages.ruby}},interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:e.languages.ruby}},punctuation:{pattern:/((?:^|\r?\n|\r)[\t ]*)[~=\-&!]+/,lookbehind:!0}};for(var t="((?:^|\\r?\\n|\\r)([\\t ]*)):{{filter_name}}(?:(?:\\r?\\n|\\r)(?:\\2[\\t ]+.+|\\s*?(?=\\r?\\n|\\r)))+",r=["css",{filter:"coffee",language:"coffeescript"},"erb","javascript","less","markdown","ruby","scss","textile"],n={},a=0,i=r.length;i>a;a++){var l=r[a];l="string"==typeof l?{filter:l,language:l}:l,e.languages[l.language]&&(n["filter-"+l.filter]={pattern:RegExp(t.replace("{{filter_name}}",l.filter)),lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"},rest:e.languages[l.language]}})}e.languages.insertBefore("haml","filter",n)}(Prism); \ No newline at end of file +!function(t){t.languages.haml={"multiline-comment":{pattern:/((?:^|\r?\n|\r)([\t ]*))(?:\/|-#).*(?:(?:\r?\n|\r)\2[\t ]+.+)*/,lookbehind:!0,alias:"comment"},"multiline-code":[{pattern:/((?:^|\r?\n|\r)([\t ]*)(?:[~-]|[&!]?=)).*,[\t ]*(?:(?:\r?\n|\r)\2[\t ]+.*,[\t ]*)*(?:(?:\r?\n|\r)\2[\t ]+.+)/,lookbehind:!0,inside:{rest:t.languages.ruby}},{pattern:/((?:^|\r?\n|\r)([\t ]*)(?:[~-]|[&!]?=)).*\|[\t ]*(?:(?:\r?\n|\r)\2[\t ]+.*\|[\t ]*)*/,lookbehind:!0,inside:{rest:t.languages.ruby}}],filter:{pattern:/((?:^|\r?\n|\r)([\t ]*)):[\w-]+(?:(?:\r?\n|\r)(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/,lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"}}},markup:{pattern:/((?:^|\r?\n|\r)[\t ]*)<.+/,lookbehind:!0,inside:{rest:t.languages.markup}},doctype:{pattern:/((?:^|\r?\n|\r)[\t ]*)!!!(?: .+)?/,lookbehind:!0},tag:{pattern:/((?:^|\r?\n|\r)[\t ]*)[%.#][\w\-#.]*[\w\-](?:\([^)]+\)|\{(?:\{[^}]+\}|[^}])+\}|\[[^\]]+\])*[\/<>]*/,lookbehind:!0,inside:{attributes:[{pattern:/(^|[^#])\{(?:\{[^}]+\}|[^}])+\}/,lookbehind:!0,inside:{rest:t.languages.ruby}},{pattern:/\([^)]+\)/,inside:{"attr-value":{pattern:/(=\s*)(?:"(?:\\.|[^\\"\r\n])*"|[^)\s]+)/,lookbehind:!0},"attr-name":/[\w:-]+(?=\s*!?=|\s*[,)])/,punctuation:/[=(),]/}},{pattern:/\[[^\]]+\]/,inside:{rest:t.languages.ruby}}],punctuation:/[<>]/}},code:{pattern:/((?:^|\r?\n|\r)[\t ]*(?:[~-]|[&!]?=)).+/,lookbehind:!0,inside:{rest:t.languages.ruby}},interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:t.languages.ruby}},punctuation:{pattern:/((?:^|\r?\n|\r)[\t ]*)[~=\-&!]+/,lookbehind:!0}};for(var e=/((?:^|\r?\n|\r)([\t ]*)):<>(?:(?:\r?\n|\r)(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/.source,r=["css",{filter:"coffee",language:"coffeescript"},"erb","javascript","less","markdown","ruby","scss","textile"],n={},a=0,i=r.length;i>a;a++){var l=r[a];l="string"==typeof l?{filter:l,language:l}:l,t.languages[l.language]&&(n["filter-"+l.filter]={pattern:t.patterns.build(e,l),lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"},rest:t.languages[l.language]}})}t.languages.insertBefore("haml","filter",n)}(Prism); \ No newline at end of file diff --git a/components/prism-pug.js b/components/prism-pug.js index e8966a37c8..2b48ff477c 100644 --- a/components/prism-pug.js +++ b/components/prism-pug.js @@ -155,7 +155,7 @@ 'punctuation': /[.\-!=|]+/ }; - var filter_pattern = '(^([\\t ]*)):{{filter_name}}(?:(?:\\r?\\n|\\r(?!\\n))(?:\\2[\\t ]+.+|\\s*?(?=\\r?\\n|\\r)))+'; + var filter_pattern = /(^([\t ]*)):<>(?:(?:\r?\n|\r(?!\n))(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/m; // Non exhaustive list of available filters and associated languages var filters = [ @@ -180,7 +180,7 @@ filter = typeof filter === 'string' ? {filter: filter, language: filter} : filter; if (Prism.languages[filter.language]) { all_filters['filter-' + filter.filter] = { - pattern: RegExp(filter_pattern.replace('{{filter_name}}', filter.filter), 'm'), + pattern: Prism.patterns.build(filter_pattern, filter), lookbehind: true, inside: { 'filter-name': { diff --git a/components/prism-pug.min.js b/components/prism-pug.min.js index 39832fc911..379c2935d5 100644 --- a/components/prism-pug.min.js +++ b/components/prism-pug.min.js @@ -1 +1 @@ -!function(e){e.languages.pug={comment:{pattern:/(^([\t ]*))\/\/.*(?:(?:\r?\n|\r)\2[\t ]+.+)*/m,lookbehind:!0},"multiline-script":{pattern:/(^([\t ]*)script\b.*\.[\t ]*)(?:(?:\r?\n|\r(?!\n))(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0,inside:{rest:e.languages.javascript}},filter:{pattern:/(^([\t ]*)):.+(?:(?:\r?\n|\r(?!\n))(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"}}},"multiline-plain-text":{pattern:/(^([\t ]*)[\w\-#.]+\.[\t ]*)(?:(?:\r?\n|\r(?!\n))(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0},markup:{pattern:/(^[\t ]*)<.+/m,lookbehind:!0,inside:{rest:e.languages.markup}},doctype:{pattern:/((?:^|\n)[\t ]*)doctype(?: .+)?/,lookbehind:!0},"flow-control":{pattern:/(^[\t ]*)(?:if|unless|else|case|when|default|each|while)\b(?: .+)?/m,lookbehind:!0,inside:{each:{pattern:/^each .+? in\b/,inside:{keyword:/\b(?:each|in)\b/,punctuation:/,/}},branch:{pattern:/^(?:if|unless|else|case|when|default|while)\b/,alias:"keyword"},rest:e.languages.javascript}},keyword:{pattern:/(^[\t ]*)(?:block|extends|include|append|prepend)\b.+/m,lookbehind:!0},mixin:[{pattern:/(^[\t ]*)mixin .+/m,lookbehind:!0,inside:{keyword:/^mixin/,"function":/\w+(?=\s*\(|\s*$)/,punctuation:/[(),.]/}},{pattern:/(^[\t ]*)\+.+/m,lookbehind:!0,inside:{name:{pattern:/^\+\w+/,alias:"function"},rest:e.languages.javascript}}],script:{pattern:/(^[\t ]*script(?:(?:&[^(]+)?\([^)]+\))*[\t ]+).+/m,lookbehind:!0,inside:{rest:e.languages.javascript}},"plain-text":{pattern:/(^[\t ]*(?!-)[\w\-#.]*[\w\-](?:(?:&[^(]+)?\([^)]+\))*\/?[\t ]+).+/m,lookbehind:!0},tag:{pattern:/(^[\t ]*)(?!-)[\w\-#.]*[\w\-](?:(?:&[^(]+)?\([^)]+\))*\/?:?/m,lookbehind:!0,inside:{attributes:[{pattern:/&[^(]+\([^)]+\)/,inside:{rest:e.languages.javascript}},{pattern:/\([^)]+\)/,inside:{"attr-value":{pattern:/(=\s*)(?:\{[^}]*\}|[^,)\r\n]+)/,lookbehind:!0,inside:{rest:e.languages.javascript}},"attr-name":/[\w-]+(?=\s*!?=|\s*[,)])/,punctuation:/[!=(),]+/}}],punctuation:/:/}},code:[{pattern:/(^[\t ]*(?:-|!?=)).+/m,lookbehind:!0,inside:{rest:e.languages.javascript}}],punctuation:/[.\-!=|]+/};for(var t="(^([\\t ]*)):{{filter_name}}(?:(?:\\r?\\n|\\r(?!\\n))(?:\\2[\\t ]+.+|\\s*?(?=\\r?\\n|\\r)))+",n=[{filter:"atpl",language:"twig"},{filter:"coffee",language:"coffeescript"},"ejs","handlebars","hogan","less","livescript","markdown","mustache","plates",{filter:"sass",language:"scss"},"stylus","swig"],a={},i=0,r=n.length;r>i;i++){var s=n[i];s="string"==typeof s?{filter:s,language:s}:s,e.languages[s.language]&&(a["filter-"+s.filter]={pattern:RegExp(t.replace("{{filter_name}}",s.filter),"m"),lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"},rest:e.languages[s.language]}})}e.languages.insertBefore("pug","filter",a)}(Prism); \ No newline at end of file +!function(e){e.languages.pug={comment:{pattern:/(^([\t ]*))\/\/.*(?:(?:\r?\n|\r)\2[\t ]+.+)*/m,lookbehind:!0},"multiline-script":{pattern:/(^([\t ]*)script\b.*\.[\t ]*)(?:(?:\r?\n|\r(?!\n))(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0,inside:{rest:e.languages.javascript}},filter:{pattern:/(^([\t ]*)):.+(?:(?:\r?\n|\r(?!\n))(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"}}},"multiline-plain-text":{pattern:/(^([\t ]*)[\w\-#.]+\.[\t ]*)(?:(?:\r?\n|\r(?!\n))(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0},markup:{pattern:/(^[\t ]*)<.+/m,lookbehind:!0,inside:{rest:e.languages.markup}},doctype:{pattern:/((?:^|\n)[\t ]*)doctype(?: .+)?/,lookbehind:!0},"flow-control":{pattern:/(^[\t ]*)(?:if|unless|else|case|when|default|each|while)\b(?: .+)?/m,lookbehind:!0,inside:{each:{pattern:/^each .+? in\b/,inside:{keyword:/\b(?:each|in)\b/,punctuation:/,/}},branch:{pattern:/^(?:if|unless|else|case|when|default|while)\b/,alias:"keyword"},rest:e.languages.javascript}},keyword:{pattern:/(^[\t ]*)(?:block|extends|include|append|prepend)\b.+/m,lookbehind:!0},mixin:[{pattern:/(^[\t ]*)mixin .+/m,lookbehind:!0,inside:{keyword:/^mixin/,"function":/\w+(?=\s*\(|\s*$)/,punctuation:/[(),.]/}},{pattern:/(^[\t ]*)\+.+/m,lookbehind:!0,inside:{name:{pattern:/^\+\w+/,alias:"function"},rest:e.languages.javascript}}],script:{pattern:/(^[\t ]*script(?:(?:&[^(]+)?\([^)]+\))*[\t ]+).+/m,lookbehind:!0,inside:{rest:e.languages.javascript}},"plain-text":{pattern:/(^[\t ]*(?!-)[\w\-#.]*[\w\-](?:(?:&[^(]+)?\([^)]+\))*\/?[\t ]+).+/m,lookbehind:!0},tag:{pattern:/(^[\t ]*)(?!-)[\w\-#.]*[\w\-](?:(?:&[^(]+)?\([^)]+\))*\/?:?/m,lookbehind:!0,inside:{attributes:[{pattern:/&[^(]+\([^)]+\)/,inside:{rest:e.languages.javascript}},{pattern:/\([^)]+\)/,inside:{"attr-value":{pattern:/(=\s*)(?:\{[^}]*\}|[^,)\r\n]+)/,lookbehind:!0,inside:{rest:e.languages.javascript}},"attr-name":/[\w-]+(?=\s*!?=|\s*[,)])/,punctuation:/[!=(),]+/}}],punctuation:/:/}},code:[{pattern:/(^[\t ]*(?:-|!?=)).+/m,lookbehind:!0,inside:{rest:e.languages.javascript}}],punctuation:/[.\-!=|]+/};for(var t=/(^([\t ]*)):<>(?:(?:\r?\n|\r(?!\n))(?:\2[\t ]+.+|\s*?(?=\r?\n|\r)))+/m,n=[{filter:"atpl",language:"twig"},{filter:"coffee",language:"coffeescript"},"ejs","handlebars","hogan","less","livescript","markdown","mustache","plates",{filter:"sass",language:"scss"},"stylus","swig"],a={},i=0,r=n.length;r>i;i++){var s=n[i];s="string"==typeof s?{filter:s,language:s}:s,e.languages[s.language]&&(a["filter-"+s.filter]={pattern:e.patterns.build(t,s),lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"},rest:e.languages[s.language]}})}e.languages.insertBefore("pug","filter",a)}(Prism); \ No newline at end of file diff --git a/components/prism-pure.js b/components/prism-pure.js index b426bb5563..cf806006ee 100644 --- a/components/prism-pure.js +++ b/components/prism-pure.js @@ -54,7 +54,7 @@ 'ats', 'dsp' ]; - var inlineLanguageRe = '%< *-\\*- *{lang}\\d* *-\\*-[\\s\\S]+?%>'; + var inlineLanguageRe = /%< *-\*- *<>\d* *-\*-[\s\S]+?%>/i; inlineLanguages.forEach(function (lang) { var alias = lang; @@ -65,7 +65,7 @@ if (Prism.languages[alias]) { var o = {}; o['inline-lang-' + alias] = { - pattern: RegExp(inlineLanguageRe.replace('{lang}', lang.replace(/([.+*?\/\\(){}\[\]])/g,'\\$1')), 'i'), + pattern: Prism.patterns.build(inlineLanguageRe, { lang: lang.replace(/([.+*?\/\\(){}\[\]])/g,'\\$1') }), inside: Prism.util.clone(Prism.languages.pure['inline-lang'].inside) }; o['inline-lang-' + alias].inside.rest = Prism.util.clone(Prism.languages[alias]); @@ -78,4 +78,4 @@ Prism.languages.pure['inline-lang'].inside.rest = Prism.util.clone(Prism.languages.c); } -}(Prism)); \ No newline at end of file +}(Prism)); diff --git a/components/prism-pure.min.js b/components/prism-pure.min.js index 474f8718b0..26f34267ff 100644 --- a/components/prism-pure.min.js +++ b/components/prism-pure.min.js @@ -1 +1 @@ -!function(e){e.languages.pure={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0},/#!.+/],"inline-lang":{pattern:/%<[\s\S]+?%>/,greedy:!0,inside:{lang:{pattern:/(^%< *)-\*-.+?-\*-/,lookbehind:!0,alias:"comment"},delimiter:{pattern:/^%<.*|%>$/,alias:"punctuation"}}},string:{pattern:/"(?:\\.|[^"\\\r\n])*"/,greedy:!0},number:{pattern:/((?:\.\.)?)(?:\b(?:inf|nan)\b|\b0x[\da-f]+|(?:\b(?:0b)?\d+(?:\.\d)?|\B\.\d)\d*(?:e[+-]?\d+)?L?)/i,lookbehind:!0},keyword:/\b(?:ans|break|bt|case|catch|cd|clear|const|def|del|dump|else|end|exit|extern|false|force|help|if|infix[lr]?|interface|let|ls|mem|namespace|nonfix|NULL|of|otherwise|outfix|override|postfix|prefix|private|public|pwd|quit|run|save|show|stats|then|throw|trace|true|type|underride|using|when|with)\b/,"function":/\b(?:abs|add_(?:(?:fundef|interface|macdef|typedef)(?:_at)?|addr|constdef|vardef)|all|any|applp?|arity|bigintp?|blob(?:_crc|_size|p)?|boolp?|byte_(?:matrix|pointer)|byte_c?string(?:_pointer)?|calloc|cat|catmap|ceil|char[ps]?|check_ptrtag|chr|clear_sentry|clearsym|closurep?|cmatrixp?|cols?|colcat(?:map)?|colmap|colrev|colvector(?:p|seq)?|complex(?:_float_(?:matrix|pointer)|_matrix(?:_view)?|_pointer|p)?|conj|cookedp?|cst|cstring(?:_(?:dup|list|vector))?|curry3?|cyclen?|del_(?:constdef|fundef|interface|macdef|typedef|vardef)|delete|diag(?:mat)?|dim|dmatrixp?|do|double(?:_matrix(?:_view)?|_pointer|p)?|dowith3?|drop|dropwhile|eval(?:cmd)?|exactp|filter|fix|fixity|flip|float(?:_matrix|_pointer)|floor|fold[lr]1?|frac|free|funp?|functionp?|gcd|get(?:_(?:byte|constdef|double|float|fundef|int(?:64)?|interface(?:_typedef)?|long|macdef|pointer|ptrtag|short|sentry|string|typedef|vardef))?|globsym|hash|head|id|im|imatrixp?|index|inexactp|infp|init|insert|int(?:_matrix(?:_view)?|_pointer|p)?|int64_(?:matrix|pointer)|integerp?|iteraten?|iterwhile|join|keys?|lambdap?|last(?:err(?:pos)?)?|lcd|list[2p]?|listmap|make_ptrtag|malloc|map|matcat|matrixp?|max|member|min|nanp|nargs|nmatrixp?|null|numberp?|ord|pack(?:ed)?|pointer(?:_cast|_tag|_type|p)?|pow|pred|ptrtag|put(?:_(?:byte|double|float|int(?:64)?|long|pointer|short|string))?|rationalp?|re|realp?|realloc|recordp?|redim|reduce(?:_with)?|refp?|repeatn?|reverse|rlistp?|round|rows?|rowcat(?:map)?|rowmap|rowrev|rowvector(?:p|seq)?|same|scan[lr]1?|sentry|sgn|short_(?:matrix|pointer)|slice|smatrixp?|sort|split|str|strcat|stream|stride|string(?:_(?:dup|list|vector)|p)?|subdiag(?:mat)?|submat|subseq2?|substr|succ|supdiag(?:mat)?|symbolp?|tail|take|takewhile|thunkp?|transpose|trunc|tuplep?|typep|ubyte|uint(?:64)?|ulong|uncurry3?|unref|unzip3?|update|ushort|vals?|varp?|vector(?:p|seq)?|void|zip3?|zipwith3?)\b/,special:{pattern:/\b__[a-z]+__\b/i,alias:"builtin"},operator:/(?=\b_|[^_])[!"#$%&'*+,\-.\/:<=>?@\\^_`|~\u00a1-\u00bf\u00d7-\u00f7\u20d0-\u2bff]+|\b(?:and|div|mod|not|or)\b/,punctuation:/[(){}\[\];,|]/};var t=["c",{lang:"c++",alias:"cpp"},"fortran","ats","dsp"],a="%< *-\\*- *{lang}\\d* *-\\*-[\\s\\S]+?%>";t.forEach(function(t){var r=t;if("string"!=typeof t&&(r=t.alias,t=t.lang),e.languages[r]){var i={};i["inline-lang-"+r]={pattern:RegExp(a.replace("{lang}",t.replace(/([.+*?\/\\(){}\[\]])/g,"\\$1")),"i"),inside:e.util.clone(e.languages.pure["inline-lang"].inside)},i["inline-lang-"+r].inside.rest=e.util.clone(e.languages[r]),e.languages.insertBefore("pure","inline-lang",i)}}),e.languages.c&&(e.languages.pure["inline-lang"].inside.rest=e.util.clone(e.languages.c))}(Prism); \ No newline at end of file +!function(e){e.languages.pure={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0},/#!.+/],"inline-lang":{pattern:/%<[\s\S]+?%>/,greedy:!0,inside:{lang:{pattern:/(^%< *)-\*-.+?-\*-/,lookbehind:!0,alias:"comment"},delimiter:{pattern:/^%<.*|%>$/,alias:"punctuation"}}},string:{pattern:/"(?:\\.|[^"\\\r\n])*"/,greedy:!0},number:{pattern:/((?:\.\.)?)(?:\b(?:inf|nan)\b|\b0x[\da-f]+|(?:\b(?:0b)?\d+(?:\.\d)?|\B\.\d)\d*(?:e[+-]?\d+)?L?)/i,lookbehind:!0},keyword:/\b(?:ans|break|bt|case|catch|cd|clear|const|def|del|dump|else|end|exit|extern|false|force|help|if|infix[lr]?|interface|let|ls|mem|namespace|nonfix|NULL|of|otherwise|outfix|override|postfix|prefix|private|public|pwd|quit|run|save|show|stats|then|throw|trace|true|type|underride|using|when|with)\b/,"function":/\b(?:abs|add_(?:(?:fundef|interface|macdef|typedef)(?:_at)?|addr|constdef|vardef)|all|any|applp?|arity|bigintp?|blob(?:_crc|_size|p)?|boolp?|byte_(?:matrix|pointer)|byte_c?string(?:_pointer)?|calloc|cat|catmap|ceil|char[ps]?|check_ptrtag|chr|clear_sentry|clearsym|closurep?|cmatrixp?|cols?|colcat(?:map)?|colmap|colrev|colvector(?:p|seq)?|complex(?:_float_(?:matrix|pointer)|_matrix(?:_view)?|_pointer|p)?|conj|cookedp?|cst|cstring(?:_(?:dup|list|vector))?|curry3?|cyclen?|del_(?:constdef|fundef|interface|macdef|typedef|vardef)|delete|diag(?:mat)?|dim|dmatrixp?|do|double(?:_matrix(?:_view)?|_pointer|p)?|dowith3?|drop|dropwhile|eval(?:cmd)?|exactp|filter|fix|fixity|flip|float(?:_matrix|_pointer)|floor|fold[lr]1?|frac|free|funp?|functionp?|gcd|get(?:_(?:byte|constdef|double|float|fundef|int(?:64)?|interface(?:_typedef)?|long|macdef|pointer|ptrtag|short|sentry|string|typedef|vardef))?|globsym|hash|head|id|im|imatrixp?|index|inexactp|infp|init|insert|int(?:_matrix(?:_view)?|_pointer|p)?|int64_(?:matrix|pointer)|integerp?|iteraten?|iterwhile|join|keys?|lambdap?|last(?:err(?:pos)?)?|lcd|list[2p]?|listmap|make_ptrtag|malloc|map|matcat|matrixp?|max|member|min|nanp|nargs|nmatrixp?|null|numberp?|ord|pack(?:ed)?|pointer(?:_cast|_tag|_type|p)?|pow|pred|ptrtag|put(?:_(?:byte|double|float|int(?:64)?|long|pointer|short|string))?|rationalp?|re|realp?|realloc|recordp?|redim|reduce(?:_with)?|refp?|repeatn?|reverse|rlistp?|round|rows?|rowcat(?:map)?|rowmap|rowrev|rowvector(?:p|seq)?|same|scan[lr]1?|sentry|sgn|short_(?:matrix|pointer)|slice|smatrixp?|sort|split|str|strcat|stream|stride|string(?:_(?:dup|list|vector)|p)?|subdiag(?:mat)?|submat|subseq2?|substr|succ|supdiag(?:mat)?|symbolp?|tail|take|takewhile|thunkp?|transpose|trunc|tuplep?|typep|ubyte|uint(?:64)?|ulong|uncurry3?|unref|unzip3?|update|ushort|vals?|varp?|vector(?:p|seq)?|void|zip3?|zipwith3?)\b/,special:{pattern:/\b__[a-z]+__\b/i,alias:"builtin"},operator:/(?=\b_|[^_])[!"#$%&'*+,\-.\/:<=>?@\\^_`|~\u00a1-\u00bf\u00d7-\u00f7\u20d0-\u2bff]+|\b(?:and|div|mod|not|or)\b/,punctuation:/[(){}\[\];,|]/};var t=["c",{lang:"c++",alias:"cpp"},"fortran","ats","dsp"],a=/%< *-\*- *<>\d* *-\*-[\s\S]+?%>/i;t.forEach(function(t){var r=t;if("string"!=typeof t&&(r=t.alias,t=t.lang),e.languages[r]){var i={};i["inline-lang-"+r]={pattern:e.patterns.build(a,{lang:t.replace(/([.+*?\/\\(){}\[\]])/g,"\\$1")}),inside:e.util.clone(e.languages.pure["inline-lang"].inside)},i["inline-lang-"+r].inside.rest=e.util.clone(e.languages[r]),e.languages.insertBefore("pure","inline-lang",i)}}),e.languages.c&&(e.languages.pure["inline-lang"].inside.rest=e.util.clone(e.languages.c))}(Prism); \ No newline at end of file diff --git a/prism.js b/prism.js index a0bc986557..710ad6067a 100644 --- a/prism.js +++ b/prism.js @@ -459,6 +459,32 @@ var _ = _self.Prism = { callback(env); } } + }, + + patterns: { + + /** + * asd + * @param {string|RegExp} basePattern + * @param {Object.} replacements + * @returns {RegExp} + */ + build: function build(basePattern, replacements) { + var placeholder = /<<([\w-]+)>>/g; + var source = basePattern.source || basePattern; + var flags = basePattern.flags || (basePattern.exec ? basePattern.toString().match(/[igmuy]*$/)[0] : ''); + + if (build.test) + build.test(basePattern, replacements, placeholder, source, flags); + + source = source.replace(placeholder, function (m, name) { + var replacement = replacements[name]; + return '(?:' + (replacement.source || replacement) + ')'; + }); + + return RegExp(source, flags); + } + } }; diff --git a/tests/helper/prism-core-tester.js b/tests/helper/prism-core-tester.js new file mode 100644 index 0000000000..71daa534fa --- /dev/null +++ b/tests/helper/prism-core-tester.js @@ -0,0 +1,52 @@ +(function (Prism) { + + Prism.patterns.build.test = function buildTest(basePattern, replacements, placeholder, source, flags) { + + // test the base pattern + + var parts = source.split(placeholder); + + for (var i = 0; i < parts.length - 1; i += 2) { + var part = parts[i]; + + // preceded by an unescaped back slash + if (/(?:^|[^\\])(?:\\\\)*\\$/.test(part)) + throw new Error('Escaped placeholder in ' + basePattern); + + // inside a char set + if (/(?:^|[^\\])(?:\\\\)*\[(?:[^\\\]]|\\.)*$/.test(part)) + throw new Error('Placeholder inside of char set in ' + basePattern); + } + + // test the replacements + + var names = {}; + for (var i = 1; i < parts.length; i += 2) + names[parts[i]] = true; + + for (var name in names) { + var replacement = replacements[name]; + + // no replacement + if (!replacement) + throw new Error('"' + name + '" does not have a replacement in ' + basePattern); + + replacement = '' + (replacement.source || replacement); + + // remove escapes + replacement = replacement.replace(/\\[^1-9]/g, ''); + + // backreferences + if (/\\[1-9]/.test(replacement)) + throw new Error('Backreference in replacement ' + name); + + // remove char sets + replacement = replacement.replace(/\[[^\]]*\]/g, ''); + + // capturing groups + if (/\((?!\?)/.test(replacement)) + throw new Error('Capturing group in replacement ' + name); + } + }; + +}(Prism)); diff --git a/tests/helper/prism-loader.js b/tests/helper/prism-loader.js index 3257485a5f..3a12a7babd 100644 --- a/tests/helper/prism-loader.js +++ b/tests/helper/prism-loader.js @@ -20,6 +20,11 @@ module.exports = { Prism: this.createEmptyPrism() }; + // add core testers + var coreTesterSource = fs.readFileSync(__dirname + "/prism-core-tester.js", "utf8"); + context.Prism = this.runFileWithContext(coreTesterSource, { Prism: context.Prism }).Prism; + + context = this.loadLanguages(languages, context); return context.Prism; @@ -73,7 +78,7 @@ module.exports = { // load the language itself var languageSource = this.loadFileSource(language); - context.Prism = this.runFileWithContext(languageSource, {Prism: context.Prism}).Prism; + context.Prism = this.runFileWithContext(languageSource, { Prism: context.Prism }).Prism; context.loadedLanguages.push(language); return context; From abc28c1bd10308d28b119675dee6aa7178edbc2e Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Wed, 22 Aug 2018 22:37:59 +0200 Subject: [PATCH 02/15] Added doc --- components/prism-core.js | 10 ++++++---- prism.js | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/components/prism-core.js b/components/prism-core.js index 33c718f17e..af6a9541b1 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -459,10 +459,12 @@ var _ = _self.Prism = { patterns: { /** - * asd - * @param {string|RegExp} basePattern - * @param {Object.} replacements - * @returns {RegExp} + * Replaces all placeholders (`<>`) in `basePattern` with `replacements[name]`. + * @param {string|RegExp} basePattern the pattern in which all placeholder are to be replaced. Placeholders are + * not allowed to be inside character sets and cannot be preceded by a unescaped backslash. + * @param {Object.} replacements the name-replacement-pairs. Replacements are not allowed + * to contain backreferences and capturing groups. + * @returns {RegExp} the base pattern with all placeholders replaced. */ build: function build(basePattern, replacements) { var placeholder = /<<([\w-]+)>>/g; diff --git a/prism.js b/prism.js index 710ad6067a..23058dd807 100644 --- a/prism.js +++ b/prism.js @@ -464,10 +464,12 @@ var _ = _self.Prism = { patterns: { /** - * asd - * @param {string|RegExp} basePattern - * @param {Object.} replacements - * @returns {RegExp} + * Replaces all placeholders (`<>`) in `basePattern` with `replacements[name]`. + * @param {string|RegExp} basePattern the pattern in which all placeholder are to be replaced. Placeholders are + * not allowed to be inside character sets and cannot be preceded by a unescaped backslash. + * @param {Object.} replacements the name-replacement-pairs. Replacements are not allowed + * to contain backreferences and capturing groups. + * @returns {RegExp} the base pattern with all placeholders replaced. */ build: function build(basePattern, replacements) { var placeholder = /<<([\w-]+)>>/g; From 865ac2c28cbff2c734f88fb6cf41bfe1b895d6f2 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Wed, 22 Aug 2018 22:45:50 +0200 Subject: [PATCH 03/15] Removed white-space change --- tests/helper/prism-loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helper/prism-loader.js b/tests/helper/prism-loader.js index 3a12a7babd..66311c536a 100644 --- a/tests/helper/prism-loader.js +++ b/tests/helper/prism-loader.js @@ -78,7 +78,7 @@ module.exports = { // load the language itself var languageSource = this.loadFileSource(language); - context.Prism = this.runFileWithContext(languageSource, { Prism: context.Prism }).Prism; + context.Prism = this.runFileWithContext(languageSource, {Prism: context.Prism}).Prism; context.loadedLanguages.push(language); return context; From 54c52fa1cc48756031daa436d7934136c25495e4 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Wed, 22 Aug 2018 23:16:07 +0200 Subject: [PATCH 04/15] Better error messages Simplified test --- tests/helper/prism-core-tester.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/helper/prism-core-tester.js b/tests/helper/prism-core-tester.js index 71daa534fa..06e1fececa 100644 --- a/tests/helper/prism-core-tester.js +++ b/tests/helper/prism-core-tester.js @@ -4,21 +4,25 @@ // test the base pattern + // 'abc<<0>>def<<1>>ghi' -> [ 'abc', '0', 'def', '1', 'ghi'] var parts = source.split(placeholder); for (var i = 0; i < parts.length - 1; i += 2) { var part = parts[i]; + // remove escapes + part = part.replace(/\\[^1-9]/g, ''); + // preceded by an unescaped back slash - if (/(?:^|[^\\])(?:\\\\)*\\$/.test(part)) - throw new Error('Escaped placeholder in ' + basePattern); + if (/\\$/.test(part)) + throw new Error('Escaped placeholder "' + parts[i + 1] + '" in ' + basePattern); - // inside a char set - if (/(?:^|[^\\])(?:\\\\)*\[(?:[^\\\]]|\\.)*$/.test(part)) - throw new Error('Placeholder inside of char set in ' + basePattern); + // inside a character set + if (/\[[^\]]*$/.test(part)) + throw new Error('Placeholder "' + parts[i + 1] + '" inside a character set in ' + basePattern); } - // test the replacements + // test the used replacements var names = {}; for (var i = 1; i < parts.length; i += 2) @@ -29,7 +33,7 @@ // no replacement if (!replacement) - throw new Error('"' + name + '" does not have a replacement in ' + basePattern); + throw new Error('There is no replacement "' + name + '" for ' + basePattern); replacement = '' + (replacement.source || replacement); @@ -38,14 +42,14 @@ // backreferences if (/\\[1-9]/.test(replacement)) - throw new Error('Backreference in replacement ' + name); + throw new Error('Backreference in replacement "' + name + '" for ' + basePattern); // remove char sets replacement = replacement.replace(/\[[^\]]*\]/g, ''); // capturing groups if (/\((?!\?)/.test(replacement)) - throw new Error('Capturing group in replacement ' + name); + throw new Error('Capturing group in replacement "' + name + '" for ' + basePattern); } }; From 1cd5ec539e609f2d034735a6027a66f974d5ee35 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Fri, 24 Aug 2018 10:41:57 +0200 Subject: [PATCH 05/15] Improved doc --- components/prism-core.js | 6 +++--- prism.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/prism-core.js b/components/prism-core.js index af6a9541b1..e51e8eb0a5 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -459,11 +459,11 @@ var _ = _self.Prism = { patterns: { /** - * Replaces all placeholders (`<>`) in `basePattern` with `replacements[name]`. + * Replaces all placeholders of the form `<>` in `basePattern` with `replacements[name]`. * @param {string|RegExp} basePattern the pattern in which all placeholder are to be replaced. Placeholders are - * not allowed to be inside character sets and cannot be preceded by a unescaped backslash. + * not allowed to be inside character sets and cannot be preceded by an unescaped backslash. * @param {Object.} replacements the name-replacement-pairs. Replacements are not allowed - * to contain backreferences and capturing groups. + * to contain backreferences or capturing groups. * @returns {RegExp} the base pattern with all placeholders replaced. */ build: function build(basePattern, replacements) { diff --git a/prism.js b/prism.js index 23058dd807..905ea9a443 100644 --- a/prism.js +++ b/prism.js @@ -464,11 +464,11 @@ var _ = _self.Prism = { patterns: { /** - * Replaces all placeholders (`<>`) in `basePattern` with `replacements[name]`. + * Replaces all placeholders of the form `<>` in `basePattern` with `replacements[name]`. * @param {string|RegExp} basePattern the pattern in which all placeholder are to be replaced. Placeholders are - * not allowed to be inside character sets and cannot be preceded by a unescaped backslash. + * not allowed to be inside character sets and cannot be preceded by an unescaped backslash. * @param {Object.} replacements the name-replacement-pairs. Replacements are not allowed - * to contain backreferences and capturing groups. + * to contain backreferences or capturing groups. * @returns {RegExp} the base pattern with all placeholders replaced. */ build: function build(basePattern, replacements) { From 24e0225ff5229586cf13ae3fa0cf5a86020065a0 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 24 Aug 2018 18:24:34 +0200 Subject: [PATCH 06/15] Improved doc --- components/prism-core.js | 3 ++- prism.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/prism-core.js b/components/prism-core.js index e51e8eb0a5..e4e35f7f1d 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -464,7 +464,8 @@ var _ = _self.Prism = { * not allowed to be inside character sets and cannot be preceded by an unescaped backslash. * @param {Object.} replacements the name-replacement-pairs. Replacements are not allowed * to contain backreferences or capturing groups. - * @returns {RegExp} the base pattern with all placeholders replaced. + * @returns {RegExp} A new regular expression with the flags of `basePattern` and its source with all + * placeholders replaced. */ build: function build(basePattern, replacements) { var placeholder = /<<([\w-]+)>>/g; diff --git a/prism.js b/prism.js index 905ea9a443..fdcf0b400d 100644 --- a/prism.js +++ b/prism.js @@ -469,7 +469,8 @@ var _ = _self.Prism = { * not allowed to be inside character sets and cannot be preceded by an unescaped backslash. * @param {Object.} replacements the name-replacement-pairs. Replacements are not allowed * to contain backreferences or capturing groups. - * @returns {RegExp} the base pattern with all placeholders replaced. + * @returns {RegExp} A new regular expression with the flags of `basePattern` and its source with all + * placeholders replaced. */ build: function build(basePattern, replacements) { var placeholder = /<<([\w-]+)>>/g; From 4b0e39081b4fe6dc69ac93ffc6ef9313069023bb Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Sun, 28 Oct 2018 19:46:18 +0100 Subject: [PATCH 07/15] HTTP: Used build for generated regex. --- components/prism-http.js | 2 +- components/prism-http.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/prism-http.js b/components/prism-http.js index 306bcd8c5d..4748b11228 100644 --- a/components/prism-http.js +++ b/components/prism-http.js @@ -39,7 +39,7 @@ for (var contentType in httpLanguages) { if (httpLanguages[contentType]) { var options = {}; options[contentType] = { - pattern: RegExp('(content-type:\\s*' + contentType + '[\\w\\W]*?)(?:\\r?\\n|\\r){2}[\\w\\W]*', 'i'), + pattern: Prism.patterns.build(/(content-type:\s*<<0>>[\w\W]*?)(?:\r?\n|\r){2}[\w\W]*/i, [contentType]), lookbehind: true, inside: { rest: httpLanguages[contentType] diff --git a/components/prism-http.min.js b/components/prism-http.min.js index 8b2c7eed07..0719ac64ca 100644 --- a/components/prism-http.min.js +++ b/components/prism-http.min.js @@ -1 +1 @@ -Prism.languages.http={"request-line":{pattern:/^(?:POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\s(?:https?:\/\/|\/)\S+\sHTTP\/[0-9.]+/m,inside:{property:/^(?:POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\b/,"attr-name":/:\w+/}},"response-status":{pattern:/^HTTP\/1.[01] \d+.*/m,inside:{property:{pattern:/(^HTTP\/1.[01] )\d+.*/i,lookbehind:!0}}},"header-name":{pattern:/^[\w-]+:(?=.)/m,alias:"keyword"}};var httpLanguages={"application/json":Prism.languages.javascript,"application/xml":Prism.languages.markup,"text/xml":Prism.languages.markup,"text/html":Prism.languages.markup};for(var contentType in httpLanguages)if(httpLanguages[contentType]){var options={};options[contentType]={pattern:RegExp("(content-type:\\s*"+contentType+"[\\w\\W]*?)(?:\\r?\\n|\\r){2}[\\w\\W]*","i"),lookbehind:!0,inside:{rest:httpLanguages[contentType]}},Prism.languages.insertBefore("http","header-name",options)} \ No newline at end of file +Prism.languages.http={"request-line":{pattern:/^(?:POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\s(?:https?:\/\/|\/)\S+\sHTTP\/[0-9.]+/m,inside:{property:/^(?:POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\b/,"attr-name":/:\w+/}},"response-status":{pattern:/^HTTP\/1.[01] \d+.*/m,inside:{property:{pattern:/(^HTTP\/1.[01] )\d+.*/i,lookbehind:!0}}},"header-name":{pattern:/^[\w-]+:(?=.)/m,alias:"keyword"}};var httpLanguages={"application/json":Prism.languages.javascript,"application/xml":Prism.languages.markup,"text/xml":Prism.languages.markup,"text/html":Prism.languages.markup};for(var contentType in httpLanguages)if(httpLanguages[contentType]){var options={};options[contentType]={pattern:Prism.patterns.build(/(content-type:\s*<<0>>[\w\W]*?)(?:\r?\n|\r){2}[\w\W]*/i,[contentType]),lookbehind:!0,inside:{rest:httpLanguages[contentType]}},Prism.languages.insertBefore("http","header-name",options)} \ No newline at end of file From 62308603a43d5cd87be3715eee327f1dcb7a0d3d Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sun, 11 Nov 2018 15:34:29 +0100 Subject: [PATCH 08/15] Updated doc --- components/prism-core.js | 6 +++--- prism.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/prism-core.js b/components/prism-core.js index f2baefc734..60a6ba1660 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -463,9 +463,9 @@ var _ = _self.Prism = { * Replaces all placeholders of the form `<>` in `basePattern` with `replacements[name]`. * @param {string|RegExp} basePattern the pattern in which all placeholder are to be replaced. Placeholders are * not allowed to be inside character sets and cannot be preceded by an unescaped backslash. - * @param {Object.} replacements the name-replacement-pairs. Replacements are not allowed - * to contain backreferences or capturing groups. - * @returns {RegExp} A new regular expression with the flags of `basePattern` and its source with all + * @param {Object.|Array.} replacements the name-replacement-pairs. + * Replacements are not allowed to contain backreferences or capturing groups. + * @returns {RegExp} A new regular expression with the flags of `basePattern` and its source with all * placeholders replaced. */ build: function build(basePattern, replacements) { diff --git a/prism.js b/prism.js index 3103e2f135..6ba70b2a32 100644 --- a/prism.js +++ b/prism.js @@ -468,9 +468,9 @@ var _ = _self.Prism = { * Replaces all placeholders of the form `<>` in `basePattern` with `replacements[name]`. * @param {string|RegExp} basePattern the pattern in which all placeholder are to be replaced. Placeholders are * not allowed to be inside character sets and cannot be preceded by an unescaped backslash. - * @param {Object.} replacements the name-replacement-pairs. Replacements are not allowed - * to contain backreferences or capturing groups. - * @returns {RegExp} A new regular expression with the flags of `basePattern` and its source with all + * @param {Object.|Array.} replacements the name-replacement-pairs. + * Replacements are not allowed to contain backreferences or capturing groups. + * @returns {RegExp} A new regular expression with the flags of `basePattern` and its source with all * placeholders replaced. */ build: function build(basePattern, replacements) { From 8347ed8d709e08435489ee0d360caf05d02f96b9 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sun, 11 Nov 2018 19:38:06 +0100 Subject: [PATCH 09/15] Textile now uses Prism.patterns.build --- components/prism-textile.js | 52 ++++++++++++++++++++------------- components/prism-textile.min.js | 2 +- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/components/prism-textile.js b/components/prism-textile.js index 8e72a117de..b760d9d177 100644 --- a/components/prism-textile.js +++ b/components/prism-textile.js @@ -24,6 +24,16 @@ }; + /** + * Creates a new RegExp using `Prism.patterns.build` and `{ mod: modifierRegex }`. + * + * @param {string|RegExp} basePattern + * @returns {RegExp} + */ + function buildWithModifier(basePattern) { + return Prism.patterns.build(basePattern, { mod: modifierRegex }); + } + Prism.languages.textile = Prism.languages.extend('markup', { 'phrase': { pattern: /(^|\r|\n)\S[\s\S]*?(?=$|\r?\n\r?\n|\r\r)/, @@ -32,10 +42,10 @@ // h1. Header 1 'block-tag': { - pattern: RegExp('^[a-z]\\w*(?:' + modifierRegex + '|[<>=()])*\\.'), + pattern: buildWithModifier('^[a-z]\\w*(?:<>|[<>=()])*\\.'), inside: { 'modifier': { - pattern: RegExp('(^[a-z]\\w*)(?:' + modifierRegex + '|[<>=()])+(?=\\.)'), + pattern: buildWithModifier('(^[a-z]\\w*)(?:<>|[<>=()])+(?=\\.)'), lookbehind: true, inside: modifierTokens }, @@ -47,10 +57,10 @@ // # List item // * List item 'list': { - pattern: RegExp('^[*#]+(?:' + modifierRegex + ')?\\s+.+', 'm'), + pattern: buildWithModifier(/^[*#]+<>?\s+.+/m), inside: { 'modifier': { - pattern: RegExp('(^[*#]+)' + modifierRegex), + pattern: buildWithModifier('(^[*#]+)<>'), lookbehind: true, inside: modifierTokens }, @@ -62,12 +72,12 @@ 'table': { // Modifiers can be applied to the row: {color:red}.|1|2|3| // or the cell: |{color:red}.1|2|3| - pattern: RegExp('^(?:(?:' + modifierRegex + '|[<>=()^~])+\\.\\s*)?(?:\\|(?:(?:' + modifierRegex + '|[<>=()^~_]|[\\\\/]\\d+)+\\.)?[^|]*)+\\|', 'm'), + pattern: buildWithModifier(/^(?:(?:<>|[<>=()^~])+\.\s*)?(?:\|(?:(?:<>|[<>=()^~_]|[\\/]\d+)+\.)?[^|]*)+\|/m), inside: { 'modifier': { // Modifiers for rows after the first one are // preceded by a pipe and a line feed - pattern: RegExp('(^|\\|(?:\\r?\\n|\\r)?)(?:' + modifierRegex + '|[<>=()^~_]|[\\\\/]\\d+)+(?=\\.)'), + pattern: buildWithModifier('(^|\\|(?:\\r?\\n|\\r)?)(?:<>|[<>=()^~_]|[\\\\/]\\d+)+(?=\\.)'), lookbehind: true, inside: modifierTokens }, @@ -76,56 +86,56 @@ }, 'inline': { - pattern: RegExp('(\\*\\*|__|\\?\\?|[*_%@+\\-^~])(?:' + modifierRegex + ')?.+?\\1'), + pattern: buildWithModifier('(\\*\\*|__|\\?\\?|[*_%@+\\-^~])<>?.+?\\1'), inside: { // Note: superscripts and subscripts are not handled specifically // *bold*, **bold** 'bold': { - pattern: RegExp('(^(\\*\\*?)(?:' + modifierRegex + ')?).+?(?=\\2)'), + pattern: buildWithModifier('(^(\\*\\*?)<>?).+?(?=\\2)'), lookbehind: true }, // _italic_, __italic__ 'italic': { - pattern: RegExp('(^(__?)(?:' + modifierRegex + ')?).+?(?=\\2)'), + pattern: buildWithModifier('(^(__?)<>?).+?(?=\\2)'), lookbehind: true }, // ??cite?? 'cite': { - pattern: RegExp('(^\\?\\?(?:' + modifierRegex + ')?).+?(?=\\?\\?)'), + pattern: buildWithModifier('(^\\?\\?<>?).+?(?=\\?\\?)'), lookbehind: true, alias: 'string' }, // @code@ 'code': { - pattern: RegExp('(^@(?:' + modifierRegex + ')?).+?(?=@)'), + pattern: buildWithModifier('(^@<>?).+?(?=@)'), lookbehind: true, alias: 'keyword' }, // +inserted+ 'inserted': { - pattern: RegExp('(^\\+(?:' + modifierRegex + ')?).+?(?=\\+)'), + pattern: buildWithModifier('(^\\+<>?).+?(?=\\+)'), lookbehind: true }, // -deleted- 'deleted': { - pattern: RegExp('(^-(?:' + modifierRegex + ')?).+?(?=-)'), + pattern: buildWithModifier('(^-<>?).+?(?=-)'), lookbehind: true }, // %span% 'span': { - pattern: RegExp('(^%(?:' + modifierRegex + ')?).+?(?=%)'), + pattern: buildWithModifier('(^%<>?).+?(?=%)'), lookbehind: true }, 'modifier': { - pattern: RegExp('(^\\*\\*|__|\\?\\?|[*_%@+\\-^~])' + modifierRegex), + pattern: buildWithModifier('(^\\*\\*|__|\\?\\?|[*_%@+\\-^~])<>'), lookbehind: true, inside: modifierTokens }, @@ -152,14 +162,14 @@ // "text":http://example.com // "text":link-ref 'link': { - pattern: RegExp('"(?:' + modifierRegex + ')?[^"]+":.+?(?=[^\\w/]?(?:\\s|$))'), + pattern: buildWithModifier('"<>?[^"]+":.+?(?=[^\\w/]?(?:\\s|$))'), inside: { 'text': { - pattern: RegExp('(^"(?:' + modifierRegex + ')?)[^"]+(?=")'), + pattern: buildWithModifier('(^"<>?)[^"]+(?=")'), lookbehind: true }, 'modifier': { - pattern: RegExp('(^")' + modifierRegex), + pattern: buildWithModifier('(^")<>'), lookbehind: true, inside: modifierTokens }, @@ -174,15 +184,15 @@ // !image.jpg! // !image.jpg(Title)!:http://example.com 'image': { - pattern: RegExp('!(?:' + modifierRegex + '|[<>=()])*[^!\\s()]+(?:\\([^)]+\\))?!(?::.+?(?=[^\\w/]?(?:\\s|$)))?'), + pattern: buildWithModifier('!(?:<>|[<>=()])*[^!\\s()]+(?:\\([^)]+\\))?!(?::.+?(?=[^\\w/]?(?:\\s|$)))?'), inside: { 'source': { - pattern: RegExp('(^!(?:' + modifierRegex + '|[<>=()])*)[^!\\s()]+(?:\\([^)]+\\))?(?=!)'), + pattern: buildWithModifier('(^!(?:<>|[<>=()])*)[^!\\s()]+(?:\\([^)]+\\))?(?=!)'), lookbehind: true, alias: 'url' }, 'modifier': { - pattern: RegExp('(^!)(?:' + modifierRegex + '|[<>=()])+'), + pattern: buildWithModifier('(^!)(?:<>|[<>=()])+'), lookbehind: true, inside: modifierTokens }, diff --git a/components/prism-textile.min.js b/components/prism-textile.min.js index 5a023dfd5d..cb69ce8b99 100644 --- a/components/prism-textile.min.js +++ b/components/prism-textile.min.js @@ -1 +1 @@ -!function(e){var i="(?:\\([^|)]+\\)|\\[[^\\]]+\\]|\\{[^}]+\\})+",n={css:{pattern:/\{[^}]+\}/,inside:{rest:e.languages.css}},"class-id":{pattern:/(\()[^)]+(?=\))/,lookbehind:!0,alias:"attr-value"},lang:{pattern:/(\[)[^\]]+(?=\])/,lookbehind:!0,alias:"attr-value"},punctuation:/[\\\/]\d+|\S/};e.languages.textile=e.languages.extend("markup",{phrase:{pattern:/(^|\r|\n)\S[\s\S]*?(?=$|\r?\n\r?\n|\r\r)/,lookbehind:!0,inside:{"block-tag":{pattern:RegExp("^[a-z]\\w*(?:"+i+"|[<>=()])*\\."),inside:{modifier:{pattern:RegExp("(^[a-z]\\w*)(?:"+i+"|[<>=()])+(?=\\.)"),lookbehind:!0,inside:n},tag:/^[a-z]\w*/,punctuation:/\.$/}},list:{pattern:RegExp("^[*#]+(?:"+i+")?\\s+.+","m"),inside:{modifier:{pattern:RegExp("(^[*#]+)"+i),lookbehind:!0,inside:n},punctuation:/^[*#]+/}},table:{pattern:RegExp("^(?:(?:"+i+"|[<>=()^~])+\\.\\s*)?(?:\\|(?:(?:"+i+"|[<>=()^~_]|[\\\\/]\\d+)+\\.)?[^|]*)+\\|","m"),inside:{modifier:{pattern:RegExp("(^|\\|(?:\\r?\\n|\\r)?)(?:"+i+"|[<>=()^~_]|[\\\\/]\\d+)+(?=\\.)"),lookbehind:!0,inside:n},punctuation:/\||^\./}},inline:{pattern:RegExp("(\\*\\*|__|\\?\\?|[*_%@+\\-^~])(?:"+i+")?.+?\\1"),inside:{bold:{pattern:RegExp("(^(\\*\\*?)(?:"+i+")?).+?(?=\\2)"),lookbehind:!0},italic:{pattern:RegExp("(^(__?)(?:"+i+")?).+?(?=\\2)"),lookbehind:!0},cite:{pattern:RegExp("(^\\?\\?(?:"+i+")?).+?(?=\\?\\?)"),lookbehind:!0,alias:"string"},code:{pattern:RegExp("(^@(?:"+i+")?).+?(?=@)"),lookbehind:!0,alias:"keyword"},inserted:{pattern:RegExp("(^\\+(?:"+i+")?).+?(?=\\+)"),lookbehind:!0},deleted:{pattern:RegExp("(^-(?:"+i+")?).+?(?=-)"),lookbehind:!0},span:{pattern:RegExp("(^%(?:"+i+")?).+?(?=%)"),lookbehind:!0},modifier:{pattern:RegExp("(^\\*\\*|__|\\?\\?|[*_%@+\\-^~])"+i),lookbehind:!0,inside:n},punctuation:/[*_%?@+\-^~]+/}},"link-ref":{pattern:/^\[[^\]]+\]\S+$/m,inside:{string:{pattern:/(\[)[^\]]+(?=\])/,lookbehind:!0},url:{pattern:/(\])\S+$/,lookbehind:!0},punctuation:/[\[\]]/}},link:{pattern:RegExp('"(?:'+i+')?[^"]+":.+?(?=[^\\w/]?(?:\\s|$))'),inside:{text:{pattern:RegExp('(^"(?:'+i+')?)[^"]+(?=")'),lookbehind:!0},modifier:{pattern:RegExp('(^")'+i),lookbehind:!0,inside:n},url:{pattern:/(:).+/,lookbehind:!0},punctuation:/[":]/}},image:{pattern:RegExp("!(?:"+i+"|[<>=()])*[^!\\s()]+(?:\\([^)]+\\))?!(?::.+?(?=[^\\w/]?(?:\\s|$)))?"),inside:{source:{pattern:RegExp("(^!(?:"+i+"|[<>=()])*)[^!\\s()]+(?:\\([^)]+\\))?(?=!)"),lookbehind:!0,alias:"url"},modifier:{pattern:RegExp("(^!)(?:"+i+"|[<>=()])+"),lookbehind:!0,inside:n},url:{pattern:/(:).+/,lookbehind:!0},punctuation:/[!:]/}},footnote:{pattern:/\b\[\d+\]/,alias:"comment",inside:{punctuation:/\[|\]/}},acronym:{pattern:/\b[A-Z\d]+\([^)]+\)/,inside:{comment:{pattern:/(\()[^)]+(?=\))/,lookbehind:!0},punctuation:/[()]/}},mark:{pattern:/\b\((?:TM|R|C)\)/,alias:"comment",inside:{punctuation:/[()]/}}}}});var t={inline:e.languages.textile.phrase.inside.inline,link:e.languages.textile.phrase.inside.link,image:e.languages.textile.phrase.inside.image,footnote:e.languages.textile.phrase.inside.footnote,acronym:e.languages.textile.phrase.inside.acronym,mark:e.languages.textile.phrase.inside.mark};e.languages.textile.tag.pattern=/<\/?(?!\d)[a-z0-9]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,e.languages.textile.phrase.inside.inline.inside.bold.inside=t,e.languages.textile.phrase.inside.inline.inside.italic.inside=t,e.languages.textile.phrase.inside.inline.inside.inserted.inside=t,e.languages.textile.phrase.inside.inline.inside.deleted.inside=t,e.languages.textile.phrase.inside.inline.inside.span.inside=t,e.languages.textile.phrase.inside.table.inside.inline=t.inline,e.languages.textile.phrase.inside.table.inside.link=t.link,e.languages.textile.phrase.inside.table.inside.image=t.image,e.languages.textile.phrase.inside.table.inside.footnote=t.footnote,e.languages.textile.phrase.inside.table.inside.acronym=t.acronym,e.languages.textile.phrase.inside.table.inside.mark=t.mark}(Prism); \ No newline at end of file +!function(e){function i(i){return e.patterns.build(i,{mod:n})}var n="(?:\\([^|)]+\\)|\\[[^\\]]+\\]|\\{[^}]+\\})+",t={css:{pattern:/\{[^}]+\}/,inside:{rest:e.languages.css}},"class-id":{pattern:/(\()[^)]+(?=\))/,lookbehind:!0,alias:"attr-value"},lang:{pattern:/(\[)[^\]]+(?=\])/,lookbehind:!0,alias:"attr-value"},punctuation:/[\\\/]\d+|\S/};e.languages.textile=e.languages.extend("markup",{phrase:{pattern:/(^|\r|\n)\S[\s\S]*?(?=$|\r?\n\r?\n|\r\r)/,lookbehind:!0,inside:{"block-tag":{pattern:i("^[a-z]\\w*(?:<>|[<>=()])*\\."),inside:{modifier:{pattern:i("(^[a-z]\\w*)(?:<>|[<>=()])+(?=\\.)"),lookbehind:!0,inside:t},tag:/^[a-z]\w*/,punctuation:/\.$/}},list:{pattern:i(/^[*#]+(?:<>)?\s+.+/m),inside:{modifier:{pattern:i("(^[*#]+)<>"),lookbehind:!0,inside:t},punctuation:/^[*#]+/}},table:{pattern:i(/^(?:(?:<>|[<>=()^~])+\.\s*)?(?:\|(?:(?:<>|[<>=()^~_]|[\\\/]\d+)+\.)?[^|]*)+\|/m),inside:{modifier:{pattern:i("(^|\\|(?:\\r?\\n|\\r)?)(?:<>|[<>=()^~_]|[\\\\/]\\d+)+(?=\\.)"),lookbehind:!0,inside:t},punctuation:/\||^\./}},inline:{pattern:i("(\\*\\*|__|\\?\\?|[*_%@+\\-^~])(?:<>)?.+?\\1"),inside:{bold:{pattern:i("(^(\\*\\*?)(?:<>)?).+?(?=\\2)"),lookbehind:!0},italic:{pattern:i("(^(__?)(?:<>)?).+?(?=\\2)"),lookbehind:!0},cite:{pattern:i("(^\\?\\?(?:<>)?).+?(?=\\?\\?)"),lookbehind:!0,alias:"string"},code:{pattern:i("(^@(?:<>)?).+?(?=@)"),lookbehind:!0,alias:"keyword"},inserted:{pattern:i("(^\\+(?:<>)?).+?(?=\\+)"),lookbehind:!0},deleted:{pattern:i("(^-(?:<>)?).+?(?=-)"),lookbehind:!0},span:{pattern:i("(^%(?:<>)?).+?(?=%)"),lookbehind:!0},modifier:{pattern:i("(^\\*\\*|__|\\?\\?|[*_%@+\\-^~])<>"),lookbehind:!0,inside:t},punctuation:/[*_%?@+\-^~]+/}},"link-ref":{pattern:/^\[[^\]]+\]\S+$/m,inside:{string:{pattern:/(\[)[^\]]+(?=\])/,lookbehind:!0},url:{pattern:/(\])\S+$/,lookbehind:!0},punctuation:/[\[\]]/}},link:{pattern:i('"(?:<>)?[^"]+":.+?(?=[^\\w/]?(?:\\s|$))'),inside:{text:{pattern:i('(^"(?:<>)?)[^"]+(?=")'),lookbehind:!0},modifier:{pattern:i('(^")<>'),lookbehind:!0,inside:t},url:{pattern:/(:).+/,lookbehind:!0},punctuation:/[":]/}},image:{pattern:i("!(?:<>|[<>=()])*[^!\\s()]+(?:\\([^)]+\\))?!(?::.+?(?=[^\\w/]?(?:\\s|$)))?"),inside:{source:{pattern:i("(^!(?:<>|[<>=()])*)[^!\\s()]+(?:\\([^)]+\\))?(?=!)"),lookbehind:!0,alias:"url"},modifier:{pattern:i("(^!)(?:<>|[<>=()])+"),lookbehind:!0,inside:t},url:{pattern:/(:).+/,lookbehind:!0},punctuation:/[!:]/}},footnote:{pattern:/\b\[\d+\]/,alias:"comment",inside:{punctuation:/\[|\]/}},acronym:{pattern:/\b[A-Z\d]+\([^)]+\)/,inside:{comment:{pattern:/(\()[^)]+(?=\))/,lookbehind:!0},punctuation:/[()]/}},mark:{pattern:/\b\((?:TM|R|C)\)/,alias:"comment",inside:{punctuation:/[()]/}}}}});var a={inline:e.languages.textile.phrase.inside.inline,link:e.languages.textile.phrase.inside.link,image:e.languages.textile.phrase.inside.image,footnote:e.languages.textile.phrase.inside.footnote,acronym:e.languages.textile.phrase.inside.acronym,mark:e.languages.textile.phrase.inside.mark};e.languages.textile.tag.pattern=/<\/?(?!\d)[a-z0-9]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,e.languages.textile.phrase.inside.inline.inside.bold.inside=a,e.languages.textile.phrase.inside.inline.inside.italic.inside=a,e.languages.textile.phrase.inside.inline.inside.inserted.inside=a,e.languages.textile.phrase.inside.inline.inside.deleted.inside=a,e.languages.textile.phrase.inside.inline.inside.span.inside=a,e.languages.textile.phrase.inside.table.inside.inline=a.inline,e.languages.textile.phrase.inside.table.inside.link=a.link,e.languages.textile.phrase.inside.table.inside.image=a.image,e.languages.textile.phrase.inside.table.inside.footnote=a.footnote,e.languages.textile.phrase.inside.table.inside.acronym=a.acronym,e.languages.textile.phrase.inside.table.inside.mark=a.mark}(Prism); \ No newline at end of file From 4e655df69d9437bb3ddce5ec04c70123a13bdd3f Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Tue, 25 Dec 2018 14:28:27 +0100 Subject: [PATCH 10/15] Added flag test criteria Improved doc and code style --- components/prism-core.js | 9 +++-- components/prism-core.min.js | 2 +- prism.js | 9 +++-- tests/helper/prism-core-tester.js | 59 +++++++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 14 deletions(-) diff --git a/components/prism-core.js b/components/prism-core.js index 25f50ad683..e8fa18fb2a 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -448,6 +448,7 @@ var _ = _self.Prism = { /** * Replaces all placeholders of the form `<>` in `basePattern` with `replacements[name]`. + * * @param {string|RegExp} basePattern the pattern in which all placeholder are to be replaced. Placeholders are * not allowed to be inside character sets and cannot be preceded by an unescaped backslash. * @param {Object.|Array.} replacements the name-replacement-pairs. @@ -458,10 +459,12 @@ var _ = _self.Prism = { build: function build(basePattern, replacements) { var placeholder = /<<([\w-]+)>>/g; var source = basePattern.source || basePattern; - var flags = basePattern.flags || (basePattern.exec ? basePattern.toString().match(/[igmuy]*$/)[0] : ''); + var flags = basePattern.flags; + if (flags === undefined) { + flags = basePattern.exec ? basePattern.toString().match(/[igmuy]*$/)[0] : ''; + } - if (build.test) - build.test(basePattern, replacements, placeholder, source, flags); + build.test && build.test(basePattern, replacements, placeholder, source, flags); source = source.replace(placeholder, function (m, name) { var replacement = replacements[name]; diff --git a/components/prism-core.min.js b/components/prism-core.min.js index 72ba99d3d1..5e22735192 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={manual:_self.Prism&&_self.Prism.manual,disableWorkerMessageHandler:_self.Prism&&_self.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof r?new r(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)return;if(!(w instanceof s)){if(m&&b!=t.length-1){h.lastIndex=k;var _=h.exec(e);if(!_)break;for(var j=_.index+(d?_[1].length:0),P=_.index+_[0].length,A=b,x=k,O=t.length;O>A&&(P>x||!t[A].type&&!t[A-1].greedy);++A)x+=t[A].length,j>=x&&(++b,k=x);if(t[b]instanceof s)continue;S=A-b,w=e.slice(k,x),_.index-=k}else{h.lastIndex=0;var _=h.exec(w),S=1}if(_){d&&(p=_[1]?_[1].length:0);var j=_.index+p,_=_[0].slice(p),P=j+_.length,I=w.slice(0,j),N=w.slice(P),C=[b,S];I&&(++b,k+=I.length,C.push(I));var E=new s(u,f?n.tokenize(_,f):_,y,_,m);if(C.push(E),N&&C.push(N),Array.prototype.splice.apply(t,C),1!=S&&n.matchGrammar(e,t,r,b,k,!0,u),l)break}else if(l)break}}}}},tokenize:function(e,t){var r=[e],a=t.rest;if(a){for(var i in a)t[i]=a[i];delete t.rest}return n.matchGrammar(e,r,t,0,0,!1),r},hooks:{all:{},add:function(e,t){var r=n.hooks.all;r[e]=r[e]||[],r[e].push(t)},run:function(e,t){var r=n.hooks.all[e];if(r&&r.length)for(var a,i=0;a=r[i++];)a(t)}},patterns:{build:function i(e,t){var n=/<<([\w-]+)>>/g,r=e.source||e,a=e.flags||(e.exec?e.toString().match(/[igmuy]*$/)[0]:"");return i.test&&i.test(e,t,n,r,a),r=r.replace(n,function(e,n){var r=t[n];return"(?:"+(r.source||r)+")"}),RegExp(r,a)}}},r=n.Token=function(e,t,n,r,a){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!a};if(r.stringify=function(e,t,a){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return r.stringify(n,t,e)}).join("");var i={type:e.type,content:r.stringify(e.content,t,a),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:a};if(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=Object.keys(i.attributes).map(function(e){return e+'="'+(i.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""},!_self.document)return _self.addEventListener?(n.disableWorkerMessageHandler||_self.addEventListener("message",function(e){var t=JSON.parse(e.data),r=t.language,a=t.code,i=t.immediateClose;_self.postMessage(n.highlight(a,n.languages[r],r)),i&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return a&&(n.filename=a.src,n.manual||a.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):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={manual:_self.Prism&&_self.Prism.manual,disableWorkerMessageHandler:_self.Prism&&_self.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof r?new r(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)return;if(!(w instanceof s)){if(m&&b!=t.length-1){h.lastIndex=k;var _=h.exec(e);if(!_)break;for(var j=_.index+(d?_[1].length:0),P=_.index+_[0].length,A=b,x=k,O=t.length;O>A&&(P>x||!t[A].type&&!t[A-1].greedy);++A)x+=t[A].length,j>=x&&(++b,k=x);if(t[b]instanceof s)continue;S=A-b,w=e.slice(k,x),_.index-=k}else{h.lastIndex=0;var _=h.exec(w),S=1}if(_){d&&(p=_[1]?_[1].length:0);var j=_.index+p,_=_[0].slice(p),P=j+_.length,I=w.slice(0,j),N=w.slice(P),C=[b,S];I&&(++b,k+=I.length,C.push(I));var E=new s(u,f?n.tokenize(_,f):_,y,_,m);if(C.push(E),N&&C.push(N),Array.prototype.splice.apply(t,C),1!=S&&n.matchGrammar(e,t,r,b,k,!0,u),l)break}else if(l)break}}}}},tokenize:function(e,t){var r=[e],a=t.rest;if(a){for(var i in a)t[i]=a[i];delete t.rest}return n.matchGrammar(e,r,t,0,0,!1),r},hooks:{all:{},add:function(e,t){var r=n.hooks.all;r[e]=r[e]||[],r[e].push(t)},run:function(e,t){var r=n.hooks.all[e];if(r&&r.length)for(var a,i=0;a=r[i++];)a(t)}},patterns:{build:function i(e,t){var n=/<<([\w-]+)>>/g,r=e.source||e,a=e.flags;return void 0===a&&(a=e.exec?e.toString().match(/[igmuy]*$/)[0]:""),i.test&&i.test(e,t,n,r,a),r=r.replace(n,function(e,n){var r=t[n];return"(?:"+(r.source||r)+")"}),RegExp(r,a)}}},r=n.Token=function(e,t,n,r,a){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!a};if(r.stringify=function(e,t,a){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return r.stringify(n,t,e)}).join("");var i={type:e.type,content:r.stringify(e.content,t,a),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:a};if(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=Object.keys(i.attributes).map(function(e){return e+'="'+(i.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""},!_self.document)return _self.addEventListener?(n.disableWorkerMessageHandler||_self.addEventListener("message",function(e){var t=JSON.parse(e.data),r=t.language,a=t.code,i=t.immediateClose;_self.postMessage(n.highlight(a,n.languages[r],r)),i&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return a&&(n.filename=a.src,n.manual||a.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):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 edf9b330c2..1fd351aca5 100644 --- a/prism.js +++ b/prism.js @@ -453,6 +453,7 @@ var _ = _self.Prism = { /** * Replaces all placeholders of the form `<>` in `basePattern` with `replacements[name]`. + * * @param {string|RegExp} basePattern the pattern in which all placeholder are to be replaced. Placeholders are * not allowed to be inside character sets and cannot be preceded by an unescaped backslash. * @param {Object.|Array.} replacements the name-replacement-pairs. @@ -463,10 +464,12 @@ var _ = _self.Prism = { build: function build(basePattern, replacements) { var placeholder = /<<([\w-]+)>>/g; var source = basePattern.source || basePattern; - var flags = basePattern.flags || (basePattern.exec ? basePattern.toString().match(/[igmuy]*$/)[0] : ''); + var flags = basePattern.flags; + if (flags === undefined) { + flags = basePattern.exec ? basePattern.toString().match(/[igmuy]*$/)[0] : ''; + } - if (build.test) - build.test(basePattern, replacements, placeholder, source, flags); + build.test && build.test(basePattern, replacements, placeholder, source, flags); source = source.replace(placeholder, function (m, name) { var replacement = replacements[name]; diff --git a/tests/helper/prism-core-tester.js b/tests/helper/prism-core-tester.js index 06e1fececa..4c9b0a6858 100644 --- a/tests/helper/prism-core-tester.js +++ b/tests/helper/prism-core-tester.js @@ -1,5 +1,27 @@ (function (Prism) { + /** + * Tests the inputs of `Prism.patterns.build` calls for the following criteria: + * + * `basePattern`: + * 1. Placeholders cannot be escaped. E.g. `foo\<>`. + * 2. Placeholders cannot be inside character sets. E.g. `foo[<>]`. + * + * `replacements`: + * 1. There has to a replacement for each placeholder. E.g. `build(/<>/, {})`. + * 2. A replacement's flags without the `g` flag have to be a subset of the flags of `basePattern`. + * E.g. `build(/foo<<0>>/, [ /[a-z]/i ])` + * 3. A replacement cannot contain backreferences. E.g. `build(/foo<<0>>/, [ /\1/ ])` + * 3. A replacement cannot contain capturing groups. E.g. `build(/<<0>>(foo)\1/, [ /(bar)/ ])` + * + * __Note:__ All strings are assumed to represent patterns without flags. + * + * @param {string|RegExp} basePattern The base pattern. + * @param {Object.|Array.} replacements The replacements. + * @param {RegExp} placeholder The regex to detect placeholders. + * @param {string} source The source of `basePattern`. + * @param {string} flags The flags of `basePattern`. + */ Prism.patterns.build.test = function buildTest(basePattern, replacements, placeholder, source, flags) { // test the base pattern @@ -14,42 +36,65 @@ part = part.replace(/\\[^1-9]/g, ''); // preceded by an unescaped back slash - if (/\\$/.test(part)) + if (/\\$/.test(part)) { throw new Error('Escaped placeholder "' + parts[i + 1] + '" in ' + basePattern); + } // inside a character set - if (/\[[^\]]*$/.test(part)) + if (/\[[^\]]*$/.test(part)) { throw new Error('Placeholder "' + parts[i + 1] + '" inside a character set in ' + basePattern); + } } // test the used replacements var names = {}; - for (var i = 1; i < parts.length; i += 2) + for (var i = 1; i < parts.length; i += 2) { names[parts[i]] = true; + } for (var name in names) { var replacement = replacements[name]; // no replacement - if (!replacement) + if (!replacement) { throw new Error('There is no replacement "' + name + '" for ' + basePattern); - + } + + // flags + // strings are assumed to have no flags + var repFlags = replacement.flags; + if (repFlags === undefined) { + repFlags = replacement.exec ? replacement.toString().match(/[igmuy]*$/)[0] : ''; + } + + // the replacement's flags have to be a subset of the base pattern's ones. (excluding g) + for (var i = repFlags.length - 1; i >= 0; i++) { + var f = repFlags[i]; + if (f !== 'g' && flags.indexOf(f) < 0) { + throw new Error('The ' + f + ' flag is present in replacement "' + name + + '" but in its base pattern ' + basePattern); + } + } + + // source replacement = '' + (replacement.source || replacement); // remove escapes replacement = replacement.replace(/\\[^1-9]/g, ''); // backreferences - if (/\\[1-9]/.test(replacement)) + if (/\\[1-9]/.test(replacement)) { throw new Error('Backreference in replacement "' + name + '" for ' + basePattern); + } // remove char sets replacement = replacement.replace(/\[[^\]]*\]/g, ''); // capturing groups - if (/\((?!\?)/.test(replacement)) + if (/\((?!\?)/.test(replacement)) { throw new Error('Capturing group in replacement "' + name + '" for ' + basePattern); + } } }; From 51468bdc8d2be37105b3d336141ea7daff40b3ec Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Tue, 25 Dec 2018 14:56:32 +0100 Subject: [PATCH 11/15] TOML uses build now --- components/prism-toml.js | 14 +++++++++----- components/prism-toml.min.js | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/components/prism-toml.js b/components/prism-toml.js index 200fbdd0c3..48a328d878 100644 --- a/components/prism-toml.js +++ b/components/prism-toml.js @@ -1,7 +1,11 @@ (function (Prism) { - // pattern: /(?:[\w-]+|'[^'\n\r]*'|"(?:\.|[^\\"\r\n])*")/ - var key = "(?:[\\w-]+|'[^'\n\r]*'|\"(?:\\.|[^\\\\\"\r\n])*\")"; + var build = Prism.patterns.build; + + var simpleString = /'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/; + + var simpleKey = build(/[\w-]+|<<0>>/, [simpleString]); + var key = build(/<<0>>(?:\s*\.\s*<<0>>)*/, [simpleKey]) Prism.languages.toml = { 'comment': { @@ -9,19 +13,19 @@ greedy: true }, 'table': { - pattern: RegExp("(\\[\\s*)" + key + "(?:\\s*\\.\\s*" + key + ")*(?=\\s*\\])"), + pattern: build(/(\[\s*)<<0>>(?=\s*\])/, [key]), lookbehind: true, greedy: true, alias: 'class-name' }, 'key': { - pattern: RegExp("(^\\s*|[{,]\\s*)" + key + "(?:\\s*\\.\\s*" + key + ")*(?=\\s*=)", "m"), + pattern: build(/(^\s*|[{,]\s*)<<0>>(?=\s*=)/m, [key]), lookbehind: true, greedy: true, alias: 'property' }, 'string': { - pattern: /"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/, + pattern: build(/"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|<<0>>/, [simpleString]), greedy: true }, 'date': [ diff --git a/components/prism-toml.min.js b/components/prism-toml.min.js index b92197f40b..f5cbf88ec8 100644 --- a/components/prism-toml.min.js +++ b/components/prism-toml.min.js @@ -1 +1 @@ -!function(e){var d="(?:[\\w-]+|'[^'\n\r]*'|\"(?:\\.|[^\\\\\"\r\n])*\")";e.languages.toml={comment:{pattern:/#.*/,greedy:!0},table:{pattern:RegExp("(\\[\\s*)"+d+"(?:\\s*\\.\\s*"+d+")*(?=\\s*\\])"),lookbehind:!0,greedy:!0,alias:"class-name"},key:{pattern:RegExp("(^\\s*|[{,]\\s*)"+d+"(?:\\s*\\.\\s*"+d+")*(?=\\s*=)","m"),lookbehind:!0,greedy:!0,alias:"property"},string:{pattern:/"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},date:[{pattern:/\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?/i,alias:"number"},{pattern:/\d{2}:\d{2}:\d{2}(?:\.\d+)?/i,alias:"number"}],number:/(?:\b0(?:x[\da-zA-Z]+(?:_[\da-zA-Z]+)*|o[0-7]+(?:_[0-7]+)*|b[10]+(?:_[10]+)*))\b|[-+]?\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?\b|[-+]?(?:inf|nan)\b/,"boolean":/\b(?:true|false)\b/,punctuation:/[.,=[\]{}]/}}(Prism); \ No newline at end of file +!function(e){var d=e.patterns.build,a=/'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/,n=d(/[\w-]+|<<0>>/,[a]),t=d(/<<0>>(?:\s*\.\s*<<0>>)*/,[n]);e.languages.toml={comment:{pattern:/#.*/,greedy:!0},table:{pattern:d(/(\[\s*)<<0>>(?=\s*\])/,[t]),lookbehind:!0,greedy:!0,alias:"class-name"},key:{pattern:d(/(^\s*|[{,]\s*)<<0>>(?=\s*=)/m,[t]),lookbehind:!0,greedy:!0,alias:"property"},string:{pattern:d(/"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|<<0>>/,[a]),greedy:!0},date:[{pattern:/\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?/i,alias:"number"},{pattern:/\d{2}:\d{2}:\d{2}(?:\.\d+)?/i,alias:"number"}],number:/(?:\b0(?:x[\da-zA-Z]+(?:_[\da-zA-Z]+)*|o[0-7]+(?:_[0-7]+)*|b[10]+(?:_[10]+)*))\b|[-+]?\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?\b|[-+]?(?:inf|nan)\b/,"boolean":/\b(?:true|false)\b/,punctuation:/[.,=[\]{}]/}}(Prism); \ No newline at end of file From df4097c38da739153c949d153a0b7e104f3e2448 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Tue, 25 Dec 2018 15:33:01 +0100 Subject: [PATCH 12/15] Better example --- tests/helper/prism-core-tester.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/helper/prism-core-tester.js b/tests/helper/prism-core-tester.js index 4c9b0a6858..b73c63d2eb 100644 --- a/tests/helper/prism-core-tester.js +++ b/tests/helper/prism-core-tester.js @@ -11,8 +11,8 @@ * 1. There has to a replacement for each placeholder. E.g. `build(/<>/, {})`. * 2. A replacement's flags without the `g` flag have to be a subset of the flags of `basePattern`. * E.g. `build(/foo<<0>>/, [ /[a-z]/i ])` - * 3. A replacement cannot contain backreferences. E.g. `build(/foo<<0>>/, [ /\1/ ])` - * 3. A replacement cannot contain capturing groups. E.g. `build(/<<0>>(foo)\1/, [ /(bar)/ ])` + * 3. A replacement cannot contain backreferences. E.g. `build(/(foo)\1<<0>>/, [ /(bar)\1/ ])` + * 3. A replacement cannot contain capturing groups. E.g. `build(/<<0>>(foo)\1/, [ /(bar)\1/ ])` * * __Note:__ All strings are assumed to represent patterns without flags. * From 53c3831d19baeaa69cc06896a0ff7698432a93f2 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Tue, 25 Dec 2018 15:52:54 +0100 Subject: [PATCH 13/15] Added example --- components/prism-core.js | 3 +++ prism.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/components/prism-core.js b/components/prism-core.js index e8fa18fb2a..1651dd37e8 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -455,6 +455,9 @@ var _ = _self.Prism = { * Replacements are not allowed to contain backreferences or capturing groups. * @returns {RegExp} A new regular expression with the flags of `basePattern` and its source with all * placeholders replaced. + * @example + * build(/^foo<>$/m, { bar: /b[Aa]r/ }) == /^foo(?:b[Aa]r)$/m + * build(/<<0>>foo<<0>>?/, [ /bar/ ]) == /(?:bar)foo(?:bar)?/ */ build: function build(basePattern, replacements) { var placeholder = /<<([\w-]+)>>/g; diff --git a/prism.js b/prism.js index 59a69db501..6de8fe1f02 100644 --- a/prism.js +++ b/prism.js @@ -460,6 +460,9 @@ var _ = _self.Prism = { * Replacements are not allowed to contain backreferences or capturing groups. * @returns {RegExp} A new regular expression with the flags of `basePattern` and its source with all * placeholders replaced. + * @example + * build(/^foo<>$/m, { bar: /b[Aa]r/ }) == /^foo(?:b[Aa]r)$/m + * build(/<<0>>foo<<0>>?/, [ /bar/ ]) == /(?:bar)foo(?:bar)?/ */ build: function build(basePattern, replacements) { var placeholder = /<<([\w-]+)>>/g; From eaa3ac85148454d88f057ffca1c00659840a47df Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Wed, 26 Dec 2018 22:02:04 +0100 Subject: [PATCH 14/15] Added tests for named capturing groups --- tests/helper/prism-core-tester.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/helper/prism-core-tester.js b/tests/helper/prism-core-tester.js index b73c63d2eb..f6acf3a49f 100644 --- a/tests/helper/prism-core-tester.js +++ b/tests/helper/prism-core-tester.js @@ -33,7 +33,7 @@ var part = parts[i]; // remove escapes - part = part.replace(/\\[^1-9]/g, ''); + part = part.replace(/\\[\s\S]/g, ''); // preceded by an unescaped back slash if (/\\$/.test(part)) { @@ -81,10 +81,10 @@ replacement = '' + (replacement.source || replacement); // remove escapes - replacement = replacement.replace(/\\[^1-9]/g, ''); + replacement = replacement.replace(/\\[^1-9k]/g, ''); // backreferences - if (/\\[1-9]/.test(replacement)) { + if (/\\(?:[1-9]|k<\w+>)/.test(replacement)) { throw new Error('Backreference in replacement "' + name + '" for ' + basePattern); } @@ -92,7 +92,7 @@ replacement = replacement.replace(/\[[^\]]*\]/g, ''); // capturing groups - if (/\((?!\?)/.test(replacement)) { + if (/\((?!\?)|\(\?<\w+>/.test(replacement)) { throw new Error('Capturing group in replacement "' + name + '" for ' + basePattern); } } From fde47f2780975d7a42a24ad08f99f8a1b4e9a62c Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sun, 6 Jan 2019 12:35:20 +0100 Subject: [PATCH 15/15] Improved doc & flag testing --- tests/helper/prism-core-tester.js | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/tests/helper/prism-core-tester.js b/tests/helper/prism-core-tester.js index f6acf3a49f..809321cd4b 100644 --- a/tests/helper/prism-core-tester.js +++ b/tests/helper/prism-core-tester.js @@ -8,11 +8,11 @@ * 2. Placeholders cannot be inside character sets. E.g. `foo[<>]`. * * `replacements`: - * 1. There has to a replacement for each placeholder. E.g. `build(/<>/, {})`. - * 2. A replacement's flags without the `g` flag have to be a subset of the flags of `basePattern`. + * 1. There has to be a replacement for each placeholder. E.g. `build(/<>/, {})`. + * 2. A replacement's flags (without the `g` and `y` flag) have to be a subset of the flags of `basePattern`. * E.g. `build(/foo<<0>>/, [ /[a-z]/i ])` * 3. A replacement cannot contain backreferences. E.g. `build(/(foo)\1<<0>>/, [ /(bar)\1/ ])` - * 3. A replacement cannot contain capturing groups. E.g. `build(/<<0>>(foo)\1/, [ /(bar)\1/ ])` + * 4. A replacement cannot contain capturing groups. E.g. `build(/<<0>>(foo)\1/, [ /(bar)\1/ ])` * * __Note:__ All strings are assumed to represent patterns without flags. * @@ -23,6 +23,21 @@ * @param {string} flags The flags of `basePattern`. */ Prism.patterns.build.test = function buildTest(basePattern, replacements, placeholder, source, flags) { + /** + * Sorts and filters the given flags, so that only the ones relevant for the pattern remain. + * + * @param {string} flags + * @returns {string} + */ + function normalizeFlags(flags) { + if (!flags) { + return ''; + } + return flags.split('').filter(f => 'gy'.indexOf(f) === -1).sort().join(''); + } + + flags = normalizeFlags(flags); + // test the base pattern @@ -67,15 +82,15 @@ if (repFlags === undefined) { repFlags = replacement.exec ? replacement.toString().match(/[igmuy]*$/)[0] : ''; } + repFlags = normalizeFlags(repFlags); - // the replacement's flags have to be a subset of the base pattern's ones. (excluding g) - for (var i = repFlags.length - 1; i >= 0; i++) { - var f = repFlags[i]; - if (f !== 'g' && flags.indexOf(f) < 0) { + // the replacement's flags have to be a subset of the base pattern's ones. + repFlags.split('').forEach(function (f) { + if (flags.indexOf(f) < 0) { throw new Error('The ' + f + ' flag is present in replacement "' + name + '" but in its base pattern ' + basePattern); } - } + }); // source replacement = '' + (replacement.source || replacement);