Mike Slinn

jekyll_pre

Published 2020-10-03. Last modified 2023-12-01.
Time to read: 5 minutes.

This page is part of the jekyll_plugins collection.

3 Tags

This Jekyll plugin provides 3 Liquid tags that work together: pre, noselect and exec.

Pre Tag

The pre block tag can display a labeled heading, a copy button, number lines, and more.

Shell
{% pre [options] %}
Contents of pre tag
{% endpre %}
Options are:

  • class="class1 class2" – Replace default CSS classes (shadow and rounded) with specified classes.
  • clear – (keyword option) Line break after floating HTML elements; ensures no floated elements overlap the rendered pre tag.
  • copyButton – (keyword option) Generate a copy button
  • dark – (keyword option) Dark mode
  • dedent – (keyword option) Remove leading spaces common to all lines, like Ruby's <<~ squiggly heredoc (default is false)
  • label="This is a label" – Apply text above pre tag. The label parameter value can also be specified in free text. The following two examples produce the same results:
    Shell
    {% pre label="This is a label" %}
    Contents of pre tag
    {% endpre %}
    Shell
    {% pre This is a label %}
    Contents of pre tag
    {% endpre %}
    Both of the above render as:
    This is a label
    Contents of pre tag
  • number – (keyword option) Number the lines
  • shell – (keyword option) Equivalent to label='Shell'
  • style – Apply CSS styles

Noselect Tag

The noselect tag renders HTML content passed to it unselectable, and generates a $ prompt if no content is provided.

Shell
{% pre %}
{% noselect [optional text string, defaults to $] %}Contents of pre tag
{% endpre %}

Exec Tag

The exec tag executes a shell command and displays the result as unselectable text. Output data is escaped, whitespace is condensed, and wrapped in the same unselectable class as the noselect tag demonstrate.

Shell
{% exec [Options] [shell command] %}
Options are:

  • cd="relative/or/absolute/directory" – Change to specified directory before executing shell command. Environment variables in the directory path will be expanded.
  • die_if_nonzero – (keyword option) Set false to treat non-zero return codes as non-fatal. Instead of terminating Jekyll with an error message, the message will be displayed as an error by the Jekyll logger, and a red message will appear in place of the result on the web page.
  • die_if_error (keyword option) – Set false to treat exceptions generated by this plugin as non-fatal. Instead of terminating Jekyll with an error message, the message will be displayed as an error by the Jekyll logger.
  • no_escape – (keyword option) Do not HTML escape the result of running the shell command.
  • no_strip – (keyword option) Do not remove leading and trailing whitespace from the result.

Keyword Options

For all keyword options, including keyword options for the pre and exec tags, option values specified in the document may be provided. If a value is not provided, the value true is assumed. Otherwise, if a value is provided, it must be wrapped in single or double quotes.

Examples

Specifying Tag Option Values

The following sets die_if_error true:

Implicitly enabling die_if_error
{% pre die_if_error %} ... {% endpre %}

The above is the same as writing:

Explicitly enabling die_if_error
{% pre die_if_error='true' %} ... {% endpre %}

Or writing:

Explicitly enabling die_if_error
{% pre die_if_error="true" %} ... {% endpre %}

Neglecting to provide surrounding quotes around the provided value causes the parser to not recognize the option. Instead, what you had intended to be the keyword/value pair will be parsed as part of the command. For the pre tag, this means the erroneous string becomes part of the label value, unless label is explicitly specified. For the exec tag, this means the erroneous string becomes part of the command to execute. The following demonstrates the error.

Pre: missing quotes around the value of die_if_error
{% pre die_if_error=false %} ... {% endpre %}

The above causes the label to be die_if_error=false.

Exec: missing quotes around the value of die_if_error
{% exec die_if_error=false ls %} ... {% endpre %}

The above causes the command to be executed to be die_if_error=false ls, instead of ls.

Installation

Add the following highlighted line to your Jekyll project's Gemfile, within the jekyll_plugins group:

Shell
group :jekyll_plugins do 
  gem 'jekyll_pre'
end 

And then execute:

Shell
$ bundle

Configuration

Default options can be specified in the standard Jekyll configuration file, _config.yml. Options are specified in the pre key. The names of each command line option is the same in the configuration file.

Option values specified in _config.yml must be provided, and the value true cannot be implied. Values that do not contain special characters may be wrapped in single or double quotes.

The following demonstrates setting a default value for every possible option. You certainly do not want to set these defaults; they are just here to show you possibilities and syntax.

Shell
pre:
  class: bg_yellow
  clear: true
  dark: true
  dedent: true
  highlight: 'Error:.*'
  label: Shell
  copyButton: true
  number: true
  style: 'font-face: courier'
  wrapper_class: rounded shadow
  wrapper_style: 'padding: 2em; border: thin green dashed;'

The default values used on mslinn.com are:

Shell
pre:
  dedent: true
  label: Shell
  copyButton: true

Specifying Default Option Values

Specifying a default value for die_if_error in _config.yml could be written any of the following ways:

Shell
pre:
  die_if_error: true
Shell
pre:
  die_if_error: "true"
Shell
pre:
  die_if_error: 'true'

Selective Clipboard Support

Specifying the pre tag’s copyButton keyword option causes a small clipboard icon to be displayed at the top right corner of the pre content. Clicking on the icon causes the contents of the pre tag to be highlighted and copied to the clipboard. Unselectable content, such as that generated by the noselect tag, is not highlighted or copied to the clipboard.

This allows readers of your content to be able to view commands and responses, but only copy commands. The selective copy makes is much faster and easier for your readers to type along with your content.

The copyButton option requires Javascript. You might want to load clipboard.js in a Jekyll layout. For example:

Shell
<script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.10/dist/clipboard.min.js"></script>

After the JavaScript loads, a new ClipboardJS instance must be created. The constructor needs to know the CSS selector for the buttons that the user will click on when they want to copy text to the clipboard. In this example, all of the buttons on the web page have class .copyBtn.

Shell
new ClipboardJS('.copyBtn');

The clipboard button is fuctionally equivalent to the following HTML:

Shell
<button class="copyBtn" data-clipboard-target="#id098814fabaf3" title="Copy to clipboard">
  <img src="/assets/images/clippy.svg" alt="Copy to clipboard" style="width: 13px">
</button>

The value of the data-clipboard-target attribute is the id of the container holding the text to be copied.

Examples

Defaults

If you have not specified default options in _config.yml, then this example will merely generate an HTML <pre> tag with the given content.

Shell
{% pre %}Contents<br>of<br>pre tag{% endpre %}

Generates:

<pre data-lt-active='false' class='maxOneScreenHigh' id='id377433c30186'>Contents<br>of<br>pre tag</pre>

Which renders as:

Contents
of
pre
tag

Dark Mode

Normally my website uses light colors, however some content displays better on a dark background. You can define the CSS any way you like.

Shell
{% pre copyButton dark label='Dark Mode Example' %}
{% noselect %}irb
{% noselect irb(main):001:0> %}p 'How now brown cow'
{% noselect How now brown cow %}
{% endpre %}

Renders as:

Dark Mode Example
$ irb
irb(main):001:0> p 'How now brown cow'
How now brown cow 

Dedent

Shell
{% pre dedent %}
    This line was indented 4 spaces
      This line was indented 6 spaces
    This line was indented 4 spaces
{% endpre %}

Which renders as:

Shell
This line was indented 4 spaces
  This line was indented 6 spaces
This line was indented 4 spaces

If you enable dedent in _config.yml, then you might want to disable it for specific instances, like this:

Shell
{% pre dedent=false %}
    This line was indented 4 spaces
      This line was indented 6 spaces
    This line was indented 4 spaces
{% endpre %}

Which renders as:

Shell
    This line was indented 4 spaces
      This line was indented 6 spaces
    This line was indented 4 spaces

CopyButton

This example generates a copy button and does not demonstrate noselect.

Shell
{% pre copyButton %}Contents<br>of<br>pre tag{% endpre %}

Generates:

<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id377433c30186'>Contents<br>of<br>pre tag</pre>

Which renders as (note the clipboard icon at the far right):

Shell
Contents
of
pre tag

CopyButton & Noselect

This example generates a copy button and demonstrates the default usage of noselect, which renders an unselectable dollar sign followed by a space.

Shell
{% pre copyButton %}
{% noselect %}Contents<br>of<br>pre tag
{% endpre %}

Generates:

<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id1e4a8fe53480'><button class='copyBtn' data-clipboard-target='#id1e4a8fe53480' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>Contents<br>of<br>pre tag</pre>

Which renders as:

Shell
$ Contents
of
pre tag

CopyButton & Noselect

This example generates a copy button and demonstrates the noselect being used twice: the first time to render an unselectable custom prompt, and the second time to render unselectable output.

Shell
{% pre copyButton %}
{% noselect >>> %}Contents of pre tag
{% noselect How now brown cow
Uselectable line 2
Line 3 %}{% endpre %}

Generates:

<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idb58a6cf1761c'><button class='copyBtn' data-clipboard-target='#idb58a6cf1761c' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>>> </span>contents of pre tag
<span class='unselectable'>How now brown cow
Uselectable line 2
Line 3</span></pre>

Which renders as:

Shell
>>> Contents of pre tag
How now brown cow
Uselectable line 2
Line 3 

Highlight

A regular expression can be passed to the highlight option. This causes text that matches the regex pattern to be wrapped within a <span class="bg_yellow"></span> tag.

The CSS stylesheet used for this page contains the following:

Shell
.bg_yellow {
  background-color: yellow;
  padding: 2px;
}

This example demonstrates highlighting text that matches a regular expression. Regular expressions match against lines, which are delimited via newlines (\n).

Shell
{% pre copyButton highlight="Line 2" %}
Line 1
  Line 2
    Line 3
      Line 4
    Line 5
  Line 6
Line 7
{% endpre %}

Which renders as:

Shell
Line 1
  Line 2
    Line 3
      Line 4
    Line 5
  Line 6
Line 7

Entire Line Highlight

Regular expressions match against lines, which are delimited via newlines (\n). Thus to match an entire line that contains a phrase, specify the regex as .*phrase.*. The following matches 3 possible phrases (2, 4 or 6), then selects the entire line if matched.

Shell
{% pre copyButton highlight=".*(2|4|6).*" %}
Line 1
  Line 2
    Line 3
      Line 4
    Line 5
  Line 6
Line 7
{% endpre %}

Which renders as:

Shell
Line 1
  Line 2
    Line 3
      Line 4
    Line 5
  Line 6
Line 7

Float Image Right

This example floats an image to the right. The jekyll_pre plugin’s clear option moves the generated HTML below the image.

Shell
<img src="jekyll.webp" style="float: right; width: 100px; height: auto;">
{% pre clear copyButton label='Clear example' %}
Using clear, copyButton and label parameters
{% endpre %}

Renders as:

Clear example
Using clear, copyButton and label parameters

Exec

The following executes ls -alF / and displays the output.

Shell
{% pre clear copyButton label='Exec without error' %}
{% noselect %}{% exec no_stderr die_if_nonzero=false ls -alF / %}
{% endpre %}

This is the rendered result:

Exec without error
$ 

Exec Error

The following executes ls -alF /invalid_directory and displays the output.

Shell
{% pre clear copyButton label='Exec without error' %}
{% noselect %}{% exec no_stderr die_if_nonzero=false ls -alF /invalid_directory %}
{% endpre %}

This is the rendered result:

Exec with error
$ 

Change Directory & Execute

The following changes to the home directory ($HOME), then executes pwd and displays the output.

Shell
{% pre clear copyButton label='Exec from $HOME' %}{% noselect %}
{% exec no_stderr cd="$HOME" die_if_nonzero=false pwd %}
{% endpre %}

Renders as:

Exec from $HOME
$ 

CSS

Below are the CSS declarations that I defined for the pre tag that produced the above output. This CSS is the same as used by flexible_include.

.clear {
  clear: both;
}

ol li .codeLabel {
  padding-left: 1.75em;
}

.error {
  color: white;
  background-color: darkred;
  padding: 2px;
}

#main-content li > div.jekyll_pre, li > div.jekyll_pre {
  margin-top: 20px;
}

.jekyll_pre + div,
.jekyll_pre + p,
.jekyll_pre + ul,
.jekyll_pre + ol,
.jekyll_pre + dl {
  margin-top: 20px;
}

.jekyll_pre + .jekyll_pre {
  margin-top: 2em;
}

.pre_tag {
  margin-bottom: 1em;
}

.tree {
  line-height: 1;
}

Comprehensive Example

The code that generates the above CSS is a good example of how the plugins work together with the from and to tags from my from_to_until plugin:

Shell
{% capture css %}{% flexible_include '_sass/mystyle.scss' %}{% endcapture %}
{% pre copyButton %}{{ css | from: '.copyBtn' | to: '^$' | strip }}

{{ css | from: '.copyContainer' | to: '^$' | strip }}

{{ css | from: '.maxOneScreenHigh' | to: '^$' | strip }}

{{ css | from: '.unselectable' | to: '^$' | strip }}
{% endpre %}


* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.