Skip to content

Commit d38592c

Browse files
atomizeatomizeRunDevelopment
authored
File highlight+data range (#1813)
Co-authored-by: atomize <[email protected]> Co-authored-by: Michael Schmidt <[email protected]>
1 parent f053af1 commit d38592c

File tree

4 files changed

+188
-45
lines changed

4 files changed

+188
-45
lines changed

plugins/file-highlight/index.html

+13
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<base href="../.." />
99
<link rel="stylesheet" href="assets/style.css" />
1010
<link rel="stylesheet" href="themes/prism.css" data-noprefix />
11+
<link rel="stylesheet" href="plugins/line-numbers/prism-line-numbers.css" data-noprefix />
1112
<script src="assets/vendor/prefixfree.min.js"></script>
1213

1314
<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
@@ -27,6 +28,14 @@ <h1>How to use</h1>
2728
<p>You don’t need to specify the language, it’s automatically determined by the file extension.
2829
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>
2930

31+
<p>Use the <code>data-range</code> attribute to display only a selected range of lines from the file, like so:</p>
32+
33+
<pre><code>&lt;pre data-src="myfile.js" data-range="1,5">&lt;/pre></code></pre>
34+
35+
<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>
36+
37+
<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>
38+
3039
<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>
3140
</section>
3241

@@ -42,13 +51,17 @@ <h1>Examples</h1>
4251
<p>File that doesn’t exist:</p>
4352
<pre data-src="foobar.js"></pre>
4453

54+
<p>With line numbers, and <code>data-range="12,111"</code>:</p>
55+
<pre data-src="plugins/file-highlight/prism-file-highlight.js" data-range="12,111" class="line-numbers"></pre>
56+
4557
<p>For more examples, browse around the Prism website. Most large code samples are actually files fetched with this plugin.</p>
4658
</section>
4759

4860
<footer data-src="assets/templates/footer.html" data-type="text/html"></footer>
4961

5062
<script src="prism.js"></script>
5163
<!-- The File Highlight plugin is already included into Prism.js build -->
64+
<script src="plugins/line-numbers/prism-line-numbers.js"></script>
5265
<script src="assets/vendor/utopia.js"></script>
5366
<script src="components.js"></script>
5467
<script src="assets/code.js"></script>

plugins/file-highlight/prism-file-highlight.js

+87-22
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,57 @@
5050
element.className = className.replace(/\s+/g, ' ').trim();
5151
}
5252

53+
/**
54+
* Loads the given file.
55+
*
56+
* @param {string} src The URL or path of the source file to load.
57+
* @param {(result: string) => void} success
58+
* @param {(reason: string) => void} error
59+
*/
60+
function loadFile(src, success, error) {
61+
var xhr = new XMLHttpRequest();
62+
xhr.open('GET', src, true);
63+
xhr.onreadystatechange = function () {
64+
if (xhr.readyState == 4) {
65+
if (xhr.status < 400 && xhr.responseText) {
66+
success(xhr.responseText);
67+
} else {
68+
if (xhr.status >= 400) {
69+
error(FAILURE_MESSAGE(xhr.status, xhr.statusText));
70+
} else {
71+
error(FAILURE_EMPTY_MESSAGE);
72+
}
73+
}
74+
}
75+
};
76+
xhr.send(null);
77+
}
78+
79+
/**
80+
* Parses the given range.
81+
*
82+
* This returns a range with inclusive ends.
83+
*
84+
* @param {string | null | undefined} range
85+
* @returns {[number, number | undefined] | undefined}
86+
*/
87+
function parseRange(range) {
88+
var m = /^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(range || '');
89+
if (m) {
90+
var start = Number(m[1]);
91+
var comma = m[2];
92+
var end = m[3];
93+
94+
if (!comma) {
95+
return [start, start];
96+
}
97+
if (!end) {
98+
return [start, undefined];
99+
}
100+
return [start, Number(end)];
101+
}
102+
return undefined;
103+
}
53104

54105
Prism.hooks.add('before-highlightall', function (env) {
55106
env.selector += ', ' + SELECTOR;
@@ -87,31 +138,45 @@
87138
}
88139

89140
// load file
90-
var xhr = new XMLHttpRequest();
91-
xhr.open('GET', src, true);
92-
xhr.onreadystatechange = function () {
93-
if (xhr.readyState == 4) {
94-
if (xhr.status < 400 && xhr.responseText) {
95-
// mark as loaded
96-
pre.setAttribute(STATUS_ATTR, STATUS_LOADED);
97-
98-
// highlight code
99-
code.textContent = xhr.responseText;
100-
Prism.highlightElement(code);
101-
102-
} else {
103-
// mark as failed
104-
pre.setAttribute(STATUS_ATTR, STATUS_FAILED);
105-
106-
if (xhr.status >= 400) {
107-
code.textContent = FAILURE_MESSAGE(xhr.status, xhr.statusText);
108-
} else {
109-
code.textContent = FAILURE_EMPTY_MESSAGE;
141+
loadFile(
142+
src,
143+
function (text) {
144+
// mark as loaded
145+
pre.setAttribute(STATUS_ATTR, STATUS_LOADED);
146+
147+
// handle data-range
148+
var range = parseRange(pre.getAttribute('data-range'));
149+
if (range) {
150+
var lines = text.split(/\r\n?|\n/g);
151+
152+
// the range is one-based and inclusive on both ends
153+
var start = range[0];
154+
var end = range[1] == null ? lines.length : range[1];
155+
156+
if (start < 0) { start += lines.length; }
157+
start = Math.max(0, Math.min(start - 1, lines.length));
158+
if (end < 0) { end += lines.length; }
159+
end = Math.max(0, Math.min(end, lines.length));
160+
161+
text = lines.slice(start, end).join('\n');
162+
163+
// add data-start for line numbers
164+
if (!pre.hasAttribute('data-start')) {
165+
pre.setAttribute('data-start', String(start + 1));
110166
}
111167
}
168+
169+
// highlight code
170+
code.textContent = text;
171+
Prism.highlightElement(code);
172+
},
173+
function (error) {
174+
// mark as failed
175+
pre.setAttribute(STATUS_ATTR, STATUS_FAILED);
176+
177+
code.textContent = error;
112178
}
113-
};
114-
xhr.send(null);
179+
);
115180
}
116181
});
117182

plugins/file-highlight/prism-file-highlight.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

prism.js

+87-22
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,57 @@ Prism.languages.js = Prism.languages.javascript;
17201720
element.className = className.replace(/\s+/g, ' ').trim();
17211721
}
17221722

1723+
/**
1724+
* Loads the given file.
1725+
*
1726+
* @param {string} src The URL or path of the source file to load.
1727+
* @param {(result: string) => void} success
1728+
* @param {(reason: string) => void} error
1729+
*/
1730+
function loadFile(src, success, error) {
1731+
var xhr = new XMLHttpRequest();
1732+
xhr.open('GET', src, true);
1733+
xhr.onreadystatechange = function () {
1734+
if (xhr.readyState == 4) {
1735+
if (xhr.status < 400 && xhr.responseText) {
1736+
success(xhr.responseText);
1737+
} else {
1738+
if (xhr.status >= 400) {
1739+
error(FAILURE_MESSAGE(xhr.status, xhr.statusText));
1740+
} else {
1741+
error(FAILURE_EMPTY_MESSAGE);
1742+
}
1743+
}
1744+
}
1745+
};
1746+
xhr.send(null);
1747+
}
1748+
1749+
/**
1750+
* Parses the given range.
1751+
*
1752+
* This returns a range with inclusive ends.
1753+
*
1754+
* @param {string | null | undefined} range
1755+
* @returns {[number, number | undefined] | undefined}
1756+
*/
1757+
function parseRange(range) {
1758+
var m = /^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(range || '');
1759+
if (m) {
1760+
var start = Number(m[1]);
1761+
var comma = m[2];
1762+
var end = m[3];
1763+
1764+
if (!comma) {
1765+
return [start, start];
1766+
}
1767+
if (!end) {
1768+
return [start, undefined];
1769+
}
1770+
return [start, Number(end)];
1771+
}
1772+
return undefined;
1773+
}
17231774

17241775
Prism.hooks.add('before-highlightall', function (env) {
17251776
env.selector += ', ' + SELECTOR;
@@ -1757,31 +1808,45 @@ Prism.languages.js = Prism.languages.javascript;
17571808
}
17581809

17591810
// load file
1760-
var xhr = new XMLHttpRequest();
1761-
xhr.open('GET', src, true);
1762-
xhr.onreadystatechange = function () {
1763-
if (xhr.readyState == 4) {
1764-
if (xhr.status < 400 && xhr.responseText) {
1765-
// mark as loaded
1766-
pre.setAttribute(STATUS_ATTR, STATUS_LOADED);
1767-
1768-
// highlight code
1769-
code.textContent = xhr.responseText;
1770-
Prism.highlightElement(code);
1771-
1772-
} else {
1773-
// mark as failed
1774-
pre.setAttribute(STATUS_ATTR, STATUS_FAILED);
1775-
1776-
if (xhr.status >= 400) {
1777-
code.textContent = FAILURE_MESSAGE(xhr.status, xhr.statusText);
1778-
} else {
1779-
code.textContent = FAILURE_EMPTY_MESSAGE;
1811+
loadFile(
1812+
src,
1813+
function (text) {
1814+
// mark as loaded
1815+
pre.setAttribute(STATUS_ATTR, STATUS_LOADED);
1816+
1817+
// handle data-range
1818+
var range = parseRange(pre.getAttribute('data-range'));
1819+
if (range) {
1820+
var lines = text.split(/\r\n?|\n/g);
1821+
1822+
// the range is one-based and inclusive on both ends
1823+
var start = range[0];
1824+
var end = range[1] == null ? lines.length : range[1];
1825+
1826+
if (start < 0) { start += lines.length; }
1827+
start = Math.max(0, Math.min(start - 1, lines.length));
1828+
if (end < 0) { end += lines.length; }
1829+
end = Math.max(0, Math.min(end, lines.length));
1830+
1831+
text = lines.slice(start, end).join('\n');
1832+
1833+
// add data-start for line numbers
1834+
if (!pre.hasAttribute('data-start')) {
1835+
pre.setAttribute('data-start', String(start + 1));
17801836
}
17811837
}
1838+
1839+
// highlight code
1840+
code.textContent = text;
1841+
Prism.highlightElement(code);
1842+
},
1843+
function (error) {
1844+
// mark as failed
1845+
pre.setAttribute(STATUS_ATTR, STATUS_FAILED);
1846+
1847+
code.textContent = error;
17821848
}
1783-
};
1784-
xhr.send(null);
1849+
);
17851850
}
17861851
});
17871852

0 commit comments

Comments
 (0)