File highlight+data range (#1813) Co-authored-by: atomize <63r71@tuta.io> Co-authored-by: Michael Schmidt <mitchi5000.ms@googlemail.com>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
diff --git a/plugins/file-highlight/index.html b/plugins/file-highlight/index.html
index 7452bf7..2033ef0 100644
--- a/plugins/file-highlight/index.html
+++ b/plugins/file-highlight/index.html
@@ -8,6 +8,7 @@
<base href="../.." />
<link rel="stylesheet" href="assets/style.css" />
<link rel="stylesheet" href="themes/prism.css" data-noprefix />
+<link rel="stylesheet" href="plugins/line-numbers/prism-line-numbers.css" data-noprefix />
<script src="assets/vendor/prefixfree.min.js"></script>
<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
@@ -27,6 +28,14 @@
<p>You don’t need to specify the language, it’s automatically determined by the file extension.
If, however, the language cannot be determined from the file extension or the file extension is incorrect, you may specify a language as well (with the usual class name way).</p>
+ <p>Use the <code>data-range</code> attribute to display only a selected range of lines from the file, like so:</p>
+
+ <pre><code><pre data-src="myfile.js" data-range="1,5"></pre></code></pre>
+
+ <p>Lines start at 1, so <code>"1,5"</code> will display line 1 up to and including line 5. It's also possible to specify just a single line (e.g. <code>"5"</code> for just line 5) and open ranges (e.g. <code>"3,"</code> for all lines starting at line 3). Negative integers can be used to specify the n-th last line, e.g. -2 for the second last line.</p>
+
+ <p>When <code>data-range</code> is used in conjunction with the <a href="plugins/line-numbers">Line Numbers plugin</a>, this plugin will add the proper <code>data-start</code> according to the specified range. This behavior can be overridden by setting the <code>data-start</code> attribute manually.</p>
+
<p>Please note that the files are fetched with XMLHttpRequest. This means that if the file is on a different origin, fetching it will fail, unless CORS is enabled on that website.</p>
</section>
@@ -42,6 +51,9 @@
<p>File that doesn’t exist:</p>
<pre data-src="foobar.js"></pre>
+ <p>With line numbers, and <code>data-range="12,111"</code>:</p>
+ <pre data-src="plugins/file-highlight/prism-file-highlight.js" data-range="12,111" class="line-numbers"></pre>
+
<p>For more examples, browse around the Prism website. Most large code samples are actually files fetched with this plugin.</p>
</section>
@@ -49,6 +61,7 @@
<script src="prism.js"></script>
<!-- The File Highlight plugin is already included into Prism.js build -->
+<script src="plugins/line-numbers/prism-line-numbers.js"></script>
<script src="assets/vendor/utopia.js"></script>
<script src="components.js"></script>
<script src="assets/code.js"></script>
diff --git a/plugins/file-highlight/prism-file-highlight.js b/plugins/file-highlight/prism-file-highlight.js
index 443dd65..0a87ac5 100644
--- a/plugins/file-highlight/prism-file-highlight.js
+++ b/plugins/file-highlight/prism-file-highlight.js
@@ -50,6 +50,57 @@
element.className = className.replace(/\s+/g, ' ').trim();
}
+ /**
+ * Loads the given file.
+ *
+ * @param {string} src The URL or path of the source file to load.
+ * @param {(result: string) => void} success
+ * @param {(reason: string) => void} error
+ */
+ function loadFile(src, success, error) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', src, true);
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ if (xhr.status < 400 && xhr.responseText) {
+ success(xhr.responseText);
+ } else {
+ if (xhr.status >= 400) {
+ error(FAILURE_MESSAGE(xhr.status, xhr.statusText));
+ } else {
+ error(FAILURE_EMPTY_MESSAGE);
+ }
+ }
+ }
+ };
+ xhr.send(null);
+ }
+
+ /**
+ * Parses the given range.
+ *
+ * This returns a range with inclusive ends.
+ *
+ * @param {string | null | undefined} range
+ * @returns {[number, number | undefined] | undefined}
+ */
+ function parseRange(range) {
+ var m = /^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(range || '');
+ if (m) {
+ var start = Number(m[1]);
+ var comma = m[2];
+ var end = m[3];
+
+ if (!comma) {
+ return [start, start];
+ }
+ if (!end) {
+ return [start, undefined];
+ }
+ return [start, Number(end)];
+ }
+ return undefined;
+ }
Prism.hooks.add('before-highlightall', function (env) {
env.selector += ', ' + SELECTOR;
@@ -87,31 +138,45 @@
}
// load file
- var xhr = new XMLHttpRequest();
- xhr.open('GET', src, true);
- xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status < 400 && xhr.responseText) {
- // mark as loaded
- pre.setAttribute(STATUS_ATTR, STATUS_LOADED);
-
- // highlight code
- code.textContent = xhr.responseText;
- Prism.highlightElement(code);
-
- } else {
- // mark as failed
- pre.setAttribute(STATUS_ATTR, STATUS_FAILED);
-
- if (xhr.status >= 400) {
- code.textContent = FAILURE_MESSAGE(xhr.status, xhr.statusText);
- } else {
- code.textContent = FAILURE_EMPTY_MESSAGE;
+ loadFile(
+ src,
+ function (text) {
+ // mark as loaded
+ pre.setAttribute(STATUS_ATTR, STATUS_LOADED);
+
+ // handle data-range
+ var range = parseRange(pre.getAttribute('data-range'));
+ if (range) {
+ var lines = text.split(/\r\n?|\n/g);
+
+ // the range is one-based and inclusive on both ends
+ var start = range[0];
+ var end = range[1] == null ? lines.length : range[1];
+
+ if (start < 0) { start += lines.length; }
+ start = Math.max(0, Math.min(start - 1, lines.length));
+ if (end < 0) { end += lines.length; }
+ end = Math.max(0, Math.min(end, lines.length));
+
+ text = lines.slice(start, end).join('\n');
+
+ // add data-start for line numbers
+ if (!pre.hasAttribute('data-start')) {
+ pre.setAttribute('data-start', String(start + 1));
}
}
+
+ // highlight code
+ code.textContent = text;
+ Prism.highlightElement(code);
+ },
+ function (error) {
+ // mark as failed
+ pre.setAttribute(STATUS_ATTR, STATUS_FAILED);
+
+ code.textContent = error;
}
- };
- xhr.send(null);
+ );
}
});
diff --git a/plugins/file-highlight/prism-file-highlight.min.js b/plugins/file-highlight/prism-file-highlight.min.js
index 24de204..b8ec530 100644
--- a/plugins/file-highlight/prism-file-highlight.min.js
+++ b/plugins/file-highlight/prism-file-highlight.min.js
@@ -1 +1 @@
-!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector);var o={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},h="data-src-status",g="loading",c="loaded",u="pre[data-src]:not(["+h+'="'+c+'"]):not(['+h+'="'+g+'"])',n=/\blang(?:uage)?-([\w-]+)\b/i;Prism.hooks.add("before-highlightall",function(e){e.selector+=", "+u}),Prism.hooks.add("before-sanity-check",function(e){var t=e.element;if(t.matches(u)){e.code="",t.setAttribute(h,g);var i=t.appendChild(document.createElement("CODE"));i.textContent="Loading…";var n=t.getAttribute("data-src"),s=e.language;if("none"===s){var a=(/\.(\w+)$/.exec(n)||[,"none"])[1];s=o[a]||a}p(i,s),p(t,s);var r=Prism.plugins.autoloader;r&&r.loadLanguages(s);var l=new XMLHttpRequest;l.open("GET",n,!0),l.onreadystatechange=function(){4==l.readyState&&(l.status<400&&l.responseText?(t.setAttribute(h,c),i.textContent=l.responseText,Prism.highlightElement(i)):(t.setAttribute(h,"failed"),400<=l.status?i.textContent=function(e,t){return"✖ Error "+e+" while fetching file: "+t}(l.status,l.statusText):i.textContent="✖ Error: File does not exist or is empty"))},l.send(null)}});var e=!(Prism.plugins.fileHighlight={highlight:function(e){for(var t,i=(e||document).querySelectorAll(u),n=0;t=i[n++];)Prism.highlightElement(t)}});Prism.fileHighlight=function(){e||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),e=!0),Prism.plugins.fileHighlight.highlight.apply(this,arguments)}}function p(e,t){var i=e.className;i=i.replace(n," ")+" language-"+t,e.className=i.replace(/\s+/g," ").trim()}}();
\ No newline at end of file
+!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector);var l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},o="data-src-status",h="loading",g="loaded",u="pre[data-src]:not(["+o+'="'+g+'"]):not(['+o+'="'+h+'"])',n=/\blang(?:uage)?-([\w-]+)\b/i;Prism.hooks.add("before-highlightall",function(t){t.selector+=", "+u}),Prism.hooks.add("before-sanity-check",function(t){var r=t.element;if(r.matches(u)){t.code="",r.setAttribute(o,h);var s=r.appendChild(document.createElement("CODE"));s.textContent="Loading…";var e=r.getAttribute("data-src"),i=t.language;if("none"===i){var n=(/\.(\w+)$/.exec(e)||[,"none"])[1];i=l[n]||n}c(s,i),c(r,i);var a=Prism.plugins.autoloader;a&&a.loadLanguages(i),function(t,e,i){var n=new XMLHttpRequest;n.open("GET",t,!0),n.onreadystatechange=function(){4==n.readyState&&(n.status<400&&n.responseText?e(n.responseText):400<=n.status?i(function(t,e){return"✖ Error "+t+" while fetching file: "+e}(n.status,n.statusText)):i("✖ Error: File does not exist or is empty"))},n.send(null)}(e,function(t){r.setAttribute(o,g);var e=function(t){var e=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(t||"");if(e){var i=Number(e[1]),n=e[2],a=e[3];return n?a?[i,Number(a)]:[i,void 0]:[i,i]}}(r.getAttribute("data-range"));if(e){var i=t.split(/\r\n?|\n/g),n=e[0],a=null==e[1]?i.length:e[1];n<0&&(n+=i.length),n=Math.max(0,Math.min(n-1,i.length)),a<0&&(a+=i.length),a=Math.max(0,Math.min(a,i.length)),t=i.slice(n,a).join("\n"),r.hasAttribute("data-start")||r.setAttribute("data-start",String(n+1))}s.textContent=t,Prism.highlightElement(s)},function(t){r.setAttribute(o,"failed"),s.textContent=t})}});var t=!(Prism.plugins.fileHighlight={highlight:function(t){for(var e,i=(t||document).querySelectorAll(u),n=0;e=i[n++];)Prism.highlightElement(e)}});Prism.fileHighlight=function(){t||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),t=!0),Prism.plugins.fileHighlight.highlight.apply(this,arguments)}}function c(t,e){var i=t.className;i=i.replace(n," ")+" language-"+e,t.className=i.replace(/\s+/g," ").trim()}}();
\ No newline at end of file
diff --git a/prism.js b/prism.js
index 2d79024..00d8ced 100644
--- a/prism.js
+++ b/prism.js
@@ -1720,6 +1720,57 @@ Prism.languages.js = Prism.languages.javascript;
element.className = className.replace(/\s+/g, ' ').trim();
}
+ /**
+ * Loads the given file.
+ *
+ * @param {string} src The URL or path of the source file to load.
+ * @param {(result: string) => void} success
+ * @param {(reason: string) => void} error
+ */
+ function loadFile(src, success, error) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', src, true);
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ if (xhr.status < 400 && xhr.responseText) {
+ success(xhr.responseText);
+ } else {
+ if (xhr.status >= 400) {
+ error(FAILURE_MESSAGE(xhr.status, xhr.statusText));
+ } else {
+ error(FAILURE_EMPTY_MESSAGE);
+ }
+ }
+ }
+ };
+ xhr.send(null);
+ }
+
+ /**
+ * Parses the given range.
+ *
+ * This returns a range with inclusive ends.
+ *
+ * @param {string | null | undefined} range
+ * @returns {[number, number | undefined] | undefined}
+ */
+ function parseRange(range) {
+ var m = /^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(range || '');
+ if (m) {
+ var start = Number(m[1]);
+ var comma = m[2];
+ var end = m[3];
+
+ if (!comma) {
+ return [start, start];
+ }
+ if (!end) {
+ return [start, undefined];
+ }
+ return [start, Number(end)];
+ }
+ return undefined;
+ }
Prism.hooks.add('before-highlightall', function (env) {
env.selector += ', ' + SELECTOR;
@@ -1757,31 +1808,45 @@ Prism.languages.js = Prism.languages.javascript;
}
// load file
- var xhr = new XMLHttpRequest();
- xhr.open('GET', src, true);
- xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status < 400 && xhr.responseText) {
- // mark as loaded
- pre.setAttribute(STATUS_ATTR, STATUS_LOADED);
-
- // highlight code
- code.textContent = xhr.responseText;
- Prism.highlightElement(code);
-
- } else {
- // mark as failed
- pre.setAttribute(STATUS_ATTR, STATUS_FAILED);
-
- if (xhr.status >= 400) {
- code.textContent = FAILURE_MESSAGE(xhr.status, xhr.statusText);
- } else {
- code.textContent = FAILURE_EMPTY_MESSAGE;
+ loadFile(
+ src,
+ function (text) {
+ // mark as loaded
+ pre.setAttribute(STATUS_ATTR, STATUS_LOADED);
+
+ // handle data-range
+ var range = parseRange(pre.getAttribute('data-range'));
+ if (range) {
+ var lines = text.split(/\r\n?|\n/g);
+
+ // the range is one-based and inclusive on both ends
+ var start = range[0];
+ var end = range[1] == null ? lines.length : range[1];
+
+ if (start < 0) { start += lines.length; }
+ start = Math.max(0, Math.min(start - 1, lines.length));
+ if (end < 0) { end += lines.length; }
+ end = Math.max(0, Math.min(end, lines.length));
+
+ text = lines.slice(start, end).join('\n');
+
+ // add data-start for line numbers
+ if (!pre.hasAttribute('data-start')) {
+ pre.setAttribute('data-start', String(start + 1));
}
}
+
+ // highlight code
+ code.textContent = text;
+ Prism.highlightElement(code);
+ },
+ function (error) {
+ // mark as failed
+ pre.setAttribute(STATUS_ATTR, STATUS_FAILED);
+
+ code.textContent = error;
}
- };
- xhr.send(null);
+ );
}
});