Mike Slinn

jekyll_pre

Published 2020-10-03. Last modified 2023-12-01.
Time to read: 6 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.

HTML or markdown
{% 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 a text label above the generated HTML pre tag. The default value is Shell. Examples:

    HTML or markdown
    {% pre label="This is a label" %}
    Contents of pre tag
    {% endpre %}

    The above renders as:

    This is a label
    Contents of pre tag

    Another example:

    HTML or markdown
    {% pre %}
    Contents of pre tag
    {% endpre %}

    The above renders as:

    Shell
    Contents of pre tag
  • number – (keyword option) Number the lines within the pre tag area.
  • shell – (keyword option) Equivalent to label='Shell'. This is the default label.
  • 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.

BNF syntax for noselect
{% pre %}
{% noselect [optional text string, defaults to $] %}More pre tag content
{% endpre %}

Example:

HTML or markdown
{% pre %}
{% noselect %}Type a command here
{% endpre %}

Renders as:

Shell
$ Type a command here

Another example:

HTML or markdown
{% pre label='irb' %}
{% noselect irb(main):001> %}
{% endpre %}

Renders as:

irb
irb(main):001> 

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.

HTML or markdown
{% 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.

Example:

HTML or markdown
{% exec date %}

Renders as:

Generated HTML
date
Mon Dec 16 13:46:16 EST 2024

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

Gem

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

Gemfile
group :jekyll_plugins do 
  gem 'jekyll_pre'
end 

And then execute:

Shell
$ bundle

CSS and Assets

Copy assets and CSS from the demo/ directory of the jekyll_pre GitHub project.

  • Copy demo/assets/images/clippy.svg to a directory of the same name in your Jekyll project.
  • Copy demo/assets/css/jekyll_plugin_support.css to your Jekyll project assets directory.
  • Copy demo/assets/css/shared_include_pre.css to your Jekyll project assets directory.
  • Copy demo/assets/css/jekyll_pre.css to your Jekyll project assets directory.
  • Incorporate the CSS stylesheets into the appropriate layout in your Jekyll project:
    _layouts/default.html
    {% assign nowMillis = site.time | date: '%s' %}
    <link rel="stylesheet" href="{{ '/assets/css/jekyll_plugin_support.css?v=' | append: nowMillis }}" type="text/css">
    <link rel="stylesheet" href="{{ '/assets/css/shared_include_pre.css?v=' | append: nowMillis }}" type="text/css">
    <link rel="stylesheet" href="{{ '/assets/css/jekyll_pre.css?v=' | append: nowMillis }}" type="text/css">

JavaScript

Copy demo/assets/js/clipboard.min.js from the jekyll_flexible_include_plugin GitHub project to your Jekyll project’s JavaScript directory.

Modify the Jekyll layout or selected pages to load the JavaScript. You can load it from your project, as shown below, or from a CDN.

One way of loading JavaScript
<script defer src="/assets/js/clipboard.min.js"></script>

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.

_config.yml fragment
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:

_config.yml fragment
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:

_config.yml
pre:
  die_if_error: true
_config.yml
pre:
  die_if_error: "true"
_config.yml
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:

HTML or markdown
<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.

Javascript
new ClipboardJS('.copyBtn');

The clipboard button is fuctionally equivalent to the following HTML:

Equivalent HTML
<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.

HTML or markdown
{% pre %}
Contents<br>of<br>pre tag
{% endpre %}

Generates:

Generated HTML
<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.

HTML or markdown
{% 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):

Rendered HTML
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.

HTML or markdown
{% 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:

$ 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.

HTML or markdown
{% 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:

Rendered HTML
>>> 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:

CSS
.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).

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

Which renders as:

Rendered output
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.

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

Which renders as:

Rendered output
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.

HTML or markdown
<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:

Exec

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

HTML or markdown
{% 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
$ ls -alF /
total 1976
drwxr-xr-x  23 root root    4096 Dec 15 14:49 ./
drwxr-xr-x  23 root root    4096 Dec 15 14:49 ../
-rw-r--r--   1 root root       0 Oct  3 04:51 .skip-cloud-init-warning
lrwxrwxrwx   1 root root       7 Apr 23  2020 bin -> usr/bin/
drwxr-xr-x   2 root root    4096 May 23  2024 boot/
drwx------   2 root root    4096 Apr  3  2022 crossdistroHHGKih/
drwx------   2 root root    4096 Feb  4  2022 crossdistroaHfIbk/
drwx------   2 root root    4096 Feb  4  2022 crossdistromBdAgG/
drwxr-xr-x  16 root root    3540 Dec 15 18:40 dev/
drwxr-xr-x 187 root root   12288 Dec 16 13:25 etc/
drwxr-xr-x   3 root root    4096 Jan 12  2022 home/
-rwxrwxrwx   1 root root 1928824 Feb 16  2024 init*
drwxr-xr-x   2 root root    4096 May  3  2022 keybase/
lrwxrwxrwx   1 root root       7 Apr 23  2020 lib -> usr/lib/
lrwxrwxrwx   1 root root       9 May 23  2024 lib32 -> usr/lib32/
lrwxrwxrwx   1 root root       9 Apr 23  2020 lib64 -> usr/lib64/
lrwxrwxrwx   1 root root      10 Apr 23  2020 libx32 -> usr/libx32/
drwx------   2 root root   16384 Apr 10  2019 lost+found/
drwxr-xr-x   2 root root    4096 Apr 23  2020 media/
drwxr-xr-x  17 root root    4096 Jun 13  2024 mnt/
drwxr-xr-x   3 root root    4096 May  3  2022 opt/
dr-xr-xr-x 446 root root       0 Dec 15 14:49 proc/
drwx------  12 root root    4096 Aug 29 12:53 root/
drwxr-xr-x  40 root root    1100 Dec 16 09:07 run/
lrwxrwxrwx   1 root root       8 Apr 23  2020 sbin -> usr/sbin/
drwxr-xr-x  18 root root    4096 May 24  2024 snap/
drwxr-xr-x   2 root root    4096 Apr 23  2020 srv/
dr-xr-xr-x  11 root root       0 Dec 15 18:41 sys/
drwxrwxrwt  18 root root     540 Dec 16 13:45 tmp/
drwxr-xr-x  14 root root    4096 Jan 21  2022 usr/
drwxr-xr-x  16 root root    4096 May 23  2024 var/

Exec Error

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

HTML or markdown
{% pre clear copyButton dedent 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
$ ls -alF /invalid_directory
Error code 2

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
$ pwd
/home/mslinn

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:

HTML or markdown
{% capture css %}{%
  flexible_include '_sass/jekyll_pre.scss'
%}{% endcapture %}
{% pre copyButton dedent label="CSS Fragment" %} {{ css | from: '.error' | to: '&rcub;' | strip }}
{{ css | from: '.pre_tag' | to: '&rcub;' | strip }} {% endpre %}

Output is:

CSS Fragment
.error {
  color: white;
  background-color: darkred;
  padding: 2px;
}
.pre_tag { margin-bottom: 1em; }
* 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.