Skip to content

Commit

Permalink
Command Line: Allow specifying output prefix using data-filter-output…
Browse files Browse the repository at this point in the history
… attribute. (#856)
  • Loading branch information
chriswells0 authored and Golmote committed Mar 26, 2018
1 parent 5f9c078 commit 094d546
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 40 deletions.
4 changes: 3 additions & 1 deletion plugins/command-line/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ <h1>How to use</h1>

<p>Add class <strong>command-line</strong> to your <code class="language-markup">&lt;pre></code>. For a server command line, specify the user and host names using the <code class="language-markup">data-user</code> and <code class="language-markup">data-host</code> attributes. The resulting prompt displays a <strong>#</strong> for the root user and <strong>$</strong> for all other users. For any other command line, such as a Windows prompt, you may specify the entire prompt using the <code class="language-markup">data-prompt</code> attribute.</p>

<p>Optional: You may specify the lines to be presented as output (no prompt) through the <code class="language-markup">data-output</code> attribute on the <code class="language-markup">&lt;pre></code> element in the following simple format:</p>
<p>Optional: You may specify the lines to be presented as output (no prompt and no highlighting) through the <code class="language-markup">data-output</code> attribute on the <code class="language-markup">&lt;pre></code> element in the following simple format:</p>
<ul>
<li>A single number refers to the line with that number</li>
<li>Ranges are denoted by two numbers, separated with a hyphen (-)</li>
Expand All @@ -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
134 changes: 96 additions & 38 deletions plugins/command-line/prism-command-line.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,101 @@ if (typeof self === 'undefined' || !self.Prism || !self.document) {
return;
}

Prism.hooks.add('complete', function (env) {
if (!env.code) {
var clsReg = /\s*\bcommand-line\b\s*/;

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;
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))
) {
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;
}

if (env.element.querySelector('.command-line-prompt')) {
// Abort if prompt already exists.
if (env.element.querySelector('.command-line-prompt')) { // Abort if prompt already exists.
env.vars['command-line'].complete = true;
return;
}

if (clsReg.test(env.element.className)) {
// Remove the class "command-line" from the <code>
env.element.className = env.element.className.replace(clsReg, '');
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 = 0; i < outputSections.length; i++) { // Parse the output sections into start/end ranges. -- cwells
var range = outputSections[i].split('-');
var outputStart = parseInt(range[0], 10);
var outputEnd = (range.length === 2 ? parseInt(range[1], 10) : 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] = '';
}
}
}
} else if (outputFilter) { // Treat lines beginning with this string as output. -- cwells
for (var i = 0; i < codeLines.length; i++) {
if (codeLines[i].indexOf(outputFilter) === 0) { // This line is output. -- cwells
env.vars['command-line'].outputLines[i] = codeLines[i].slice(outputFilter.length);
codeLines[i] = '';
}
}
}

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

Prism.hooks.add('before-insert', function (env) {
env.vars = env.vars || {};
env.vars['command-line'] = env.vars['command-line'] || {};
if (env.vars['command-line'].complete) {
return;
}
if (!clsReg.test(pre.className)) {
// Add the class "command-line" to the <pre>

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

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

var pre = env.element.parentNode;
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>
pre.className += ' command-line';
}

Expand All @@ -39,43 +107,33 @@ Prism.hooks.add('complete', function (env) {
};

// Create the "rows" that will become the command-line prompts. -- cwells
var lines = 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 !== '') {
lines = lines.join('<span data-prompt="' + promptText + '"></span>');
promptLines = promptLines.join('<span data-prompt="' + promptText + '"></span>');
} else {
var user = getAttribute('data-user', 'user');
var host = getAttribute('data-host', 'localhost');
lines = lines.join('<span data-user="' + user + '" data-host="' + host + '"></span>');
promptLines = promptLines.join('<span data-user="' + user + '" data-host="' + host + '"></span>');
}

// Create the wrapper element. -- cwells
var prompt = document.createElement('span');
prompt.className = 'command-line-prompt';
prompt.innerHTML = lines;

// Mark the output lines so they can be styled differently (no prompt). -- cwells
var outputSections = pre.getAttribute('data-output') || '';
outputSections = outputSections.split(',');
for (var i = 0; i < outputSections.length; i++) {
var outputRange = outputSections[i].split('-');
var outputStart = parseInt(outputRange[0]);
var outputEnd = outputStart; // Default: end at the first line when it's not an actual range. -- cwells
if (outputRange.length === 2) {
outputEnd = parseInt(outputRange[1]);
}
prompt.innerHTML = promptLines;

if (!isNaN(outputStart) && !isNaN(outputEnd)) {
for (var j = outputStart; j <= outputEnd && j <= prompt.children.length; j++) {
var node = prompt.children[j - 1];
node.removeAttribute('data-user');
node.removeAttribute('data-host');
node.removeAttribute('data-prompt');
}
// Remove the prompt from the output lines. -- cwells
for (var i = 0; i < env.vars['command-line'].outputLines.length; i++) {
if (env.vars['command-line'].outputLines.hasOwnProperty(i)) {
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 094d546

Please sign in to comment.