Skip to content

Commit

Permalink
Allow specifying output prefix using data-filter-output attribute.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswells0 committed Mar 25, 2018
1 parent 8cf3b4a commit 51a9f5d
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 67 deletions.
2 changes: 2 additions & 0 deletions plugins/command-line/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ <h1>How to use</h1>
<dt>1-2, 5, 9-20</dt>
<dd>Lines 1 through 2, line 5, lines 9 through 20</dd>
</dl>

<p>Optional: To automatically present some lines as output, you can prefix those lines with any string and specify the prefix using the <code class="language-markup">data-filter-output</code> attribute on the <code class="language-markup">&lt;pre></code> element. For example, <code class="language-markup">data-filter-output="(out)"</code> will treat lines beginning with <code class="language-markup">(out)</code> as output and remove the prefix.</p>
</section>

<section>
Expand Down
136 changes: 70 additions & 66 deletions plugins/command-line/prism-command-line.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,99 @@ if (typeof self === 'undefined' || !self.Prism || !self.document) {
return;
}

// Scoped to this IIFE so they can be accessed by both hooks. -- cwells
var outputLines = [];
var outputRanges = [];

Prism.hooks.add('before-highlight', function (env) {
env.vars = env.vars || {};
env.vars['command-line'] = env.vars['command-line'] || {};

if (env.vars['command-line'].complete || !env.code) {
env.vars['command-line'].complete = true;
return;
}

// Works only for <code> wrapped inside <pre> (not inline).
var pre = env.element.parentNode;
if (!pre || !/pre/i.test(pre.nodeName)) {
var clsReg = /\s*\bcommand-line\b\s*/;
if (!pre || !/pre/i.test(pre.nodeName) || // Abort only if neither the <pre> nor the <code> have the class
(!clsReg.test(pre.className) && !clsReg.test(env.element.className))) {
env.vars['command-line'].complete = true;
return;
}

// Reset outputLines and outputRanges for each env. -- cwells
outputLines = [];
outputRanges = [];
var codeLines = env.code.split('\n');
if (env.element.querySelector('.command-line-prompt')) { // Abort if prompt already exists.
env.vars['command-line'].complete = true;
return;
}

// Parse the output sections into start/end ranges. -- cwells
var outputSections = pre.getAttribute('data-output') || '';
outputSections = outputSections.split(',');
for (var i = 0; i < outputSections.length; i++) {
var range = outputSections[i].split('-');
var outputStart = parseInt(range[0]);
var outputEnd = (range.length === 2 ? parseInt(range[1]) : outputStart);

if (!isNaN(outputStart) && !isNaN(outputEnd)) {
if (outputStart < 1) {
outputStart = 1;
var codeLines = env.code.split('\n');
env.vars['command-line'].numberOfLines = codeLines.length;
env.vars['command-line'].outputLines = [];

var outputSections = pre.getAttribute('data-output');
var outputFilter = pre.getAttribute('data-filter-output');
if (outputSections || outputSections === '') { // The user specified the output lines. -- cwells
outputSections = outputSections.split(',');
for (var i in outputSections) { // Parse the output sections into start/end ranges. -- cwells
var range = outputSections[i].split('-');
var outputStart = parseInt(range[0]);
var outputEnd = (range.length === 2 ? parseInt(range[1]) : outputStart);

if (!isNaN(outputStart) && !isNaN(outputEnd)) {
if (outputStart < 1) {
outputStart = 1;
}
if (outputEnd > codeLines.length) {
outputEnd = codeLines.length;
}
// Convert start and end to 0-based to simplify the arrays. -- cwells
outputStart--;
outputEnd--;
// Save the output line in an array and clear it in the code so it's not highlighted. -- cwells
for (var j = outputStart; j <= outputEnd; j++) {
env.vars['command-line'].outputLines[j] = codeLines[j];
codeLines[j] = '';
}
}
if (outputEnd > codeLines.length) {
outputEnd = codeLines.length;
}
} else if (outputFilter) { // Treat lines beginning with this string as output. -- cwells
for (var i in codeLines) {
if (codeLines[i].indexOf(outputFilter) === 0) { // This line is output. -- cwells
env.vars['command-line'].outputLines[i] = codeLines[i].slice(outputFilter.length);
codeLines[i] = '';
}
// Reindex start and end to 0-based as we create the object. -- cwells
outputRanges.push({ start: --outputStart, end: --outputEnd });
}
}

// Temporarily remove the output lines to prevent highlighting. -- cwells
outputRanges.forEach(function (range) {
for (var i = range.start; i <= range.end; i++) {
// Save the output line in a temp array and clear it in the code so it's not highlighted. -- cwells
outputLines[i] = codeLines[i];
codeLines[i] = '';
}
});

env.code = codeLines.join('\n');
});

Prism.hooks.add('before-insert', function (env) {
var pre = env.element.parentNode;
if (!pre || !/pre/i.test(pre.nodeName)) {
env.vars = env.vars || {};
env.vars['command-line'] = env.vars['command-line'] || {};
if (env.vars['command-line'].complete) {
return;
}

// Reinsert the output lines into the highlighted code. -- cwells
var codeLines = env.highlightedCode.split('\n');
for (var i in outputLines) {
codeLines[i] = outputLines[i];
for (var i in env.vars['command-line'].outputLines) {
codeLines[i] = env.vars['command-line'].outputLines[i];
}
env.highlightedCode = codeLines.join('\n');
});

Prism.hooks.add('complete', function (env) {
if (!env.code) {
env.vars = env.vars || {};
env.vars['command-line'] = env.vars['command-line'] || {};
if (env.vars['command-line'].complete) {
return;
}

// Works only for <code> wrapped inside <pre> (not inline).
var pre = env.element.parentNode;
var clsReg = /\s*\bcommand-line\b\s*/;
if (
!pre || !/pre/i.test(pre.nodeName) ||
// Abort only if neither the <pre> nor the <code> have the class
(!clsReg.test(pre.className) && !clsReg.test(env.element.className))
) {
return;
}

if (env.element.querySelector('.command-line-prompt')) {
// Abort if prompt already exists.
return;
}

if (clsReg.test(env.element.className)) {
// Remove the class "command-line" from the <code>
if (clsReg.test(env.element.className)) { // Remove the class "command-line" from the <code>
env.element.className = env.element.className.replace(clsReg, '');
}
if (!clsReg.test(pre.className)) {
// Add the class "command-line" to the <pre>
if (!clsReg.test(pre.className)) { // Add the class "command-line" to the <pre>
pre.className += ' command-line';
}

Expand All @@ -100,7 +105,7 @@ Prism.hooks.add('complete', function (env) {
};

// Create the "rows" that will become the command-line prompts. -- cwells
var promptLines = new Array(1 + env.code.split('\n').length);
var promptLines = new Array(env.vars['command-line'].numberOfLines + 1);
var promptText = getAttribute('data-prompt', '');
if (promptText !== '') {
promptLines = promptLines.join('<span data-prompt="' + promptText + '"></span>');
Expand All @@ -116,16 +121,15 @@ Prism.hooks.add('complete', function (env) {
prompt.innerHTML = promptLines;

// Remove the prompt from the output lines. -- cwells
outputRanges.forEach(function (range) {
for (var i = range.start; i <= range.end; i++) {
var node = prompt.children[i];
node.removeAttribute('data-user');
node.removeAttribute('data-host');
node.removeAttribute('data-prompt');
}
});
for (var i in env.vars['command-line'].outputLines) {
var node = prompt.children[i];
node.removeAttribute('data-user');
node.removeAttribute('data-host');
node.removeAttribute('data-prompt');
}

env.element.innerHTML = prompt.outerHTML + env.element.innerHTML;
env.element.insertBefore(prompt, env.element.firstChild);
env.vars['command-line'].complete = true;
});

}());
2 changes: 1 addition & 1 deletion plugins/command-line/prism-command-line.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 51a9f5d

Please sign in to comment.