Skip to content

Formatting and Syntax Highlighting

Jakub T. Jankiewicz edited this page May 14, 2024 · 55 revisions

Formatting

Formatting the output of the echo method uses a special syntax that can be used to format your text; you can make your text bold, italic or underlined, or even glow. Because it uses the special syntax you don't need to worry about XSS Vulnerability.

Basic example:

term.echo('[[b;red;white]hello world]');

This will create a red bold text on a white background.

More about the syntax of Terminal formatting in API reference.

If you need to echo brackets or backslash ([ ] \), the best way is to use the $.terminal.escape_brackets function or an alias $.terminal.escape_formatting.

term.echo('[[;red;]' + $.terminal.escape_formatting(message) + ']');

Links

To render links you can just echo URL or using formatting syntax:

term.echo('[[!;;;;https://github.com/jcubic/jquery.terminal]visit repo]');

Images

To render images you can use the same formatting syntax using @ character as the first style option in brackets:

term.echo('[[@;;;;https://placekitten.com/300/300]]');

Images handing was added in version 2.8.0, with more updates in 2.9.0.

Syntax highlighting

If you want to define syntax highlighting for instance taken from python or SQL, there is the possibility to not have to specify this formatting on your own but use the PrismJS library. This will also work while you type. To use PrismJS, you first need to include the files needed for the library:

https://cdn.jsdelivr.net/npm/prismjs/prism.js
https://cdn.jsdelivr.net/npm/prismjs/themes/prism.css

by default PrismJS only includes the HTML, CSS, and JavaScript syntax. If you need others like python or SQL, you need to include one of the component files; they are also on npm and on GitHub.

NOTE: default prism doesn't work 100% as it should that's why there is a copy of the CSS files in terminal-prism repo, that adds CSS variables to Prism (the code is a simple script that adds CSS Variables to existing PrismJS).

So you can use this, as a replacement for PrismJS CSS:

https://cdn.jsdelivr.net/npm/terminal-prism/css/prism.css

You can also use different theme, just open directory and try them out:

https://cdn.jsdelivr.net/npm/terminal-prism/css/

After you have included the PrismJS files, you also need to include the jQuery Terminal Prism wrapper:

https://cdn.jsdelivr.net/npm/jquery.terminal/js/prism.js

Finally, you can call the syntax function somewhere after the terminal and PrismJS files have been "imported". It doesn't need to be in $(function() {});. To get python highlighting, as an example:

$.terminal.syntax('python')

There is one additional syntax highlighting added by the Terminal prism wrapper (called website) which is to highlight HTML, CSS, and javascript. This is HTML that include mix of CSS and JS that are defined by the <style>...</style> and `<script>...</script> tags.

$.terminal.syntax('website');

Syntax highlighters are also useful if you want to have a command like cat or less where you show the content of the files.

If you want to limit where should syntax highlighting be applied you can use:

$.terminal.prism_formatters = {
    prompt: true,
    echo: true,
    animation: true, // will be supported in version >= 2.32.0
    command: true
};

ANSI Escape codes

ANSI escape codes are a way to format text most commonly seen in Unix terminals. The formatting looks like this:

\x1b[32mHello\x1b[m

This will display the text Hello in green. 1B is the hexadecimal for 27 which is the escape key.

Explanation of the ANSI ESCAPE codes can be found on Wikipedia.

To use ANSI formatting in jQuery Terminal, you only need to include one file:

https://cdn.jsdelivr.net/npm/jquery.terminal/js/unix_formatting.js

The file also handles what's called overtyping (which is used, for example, in the output of the man command on Linux/Unix), where you have text like this

A\x08AB\x08BC\x08C

which will display ABC in bold text. \x08 is the code for the backspace key; so you simply write two times the same character to make it bold; if you use A\x08_ you will get underline instead. Backspace (\x08 characters) should work the same as in the Linux terminal.

See also rendering of ANSI art demo.

From version 2.27.0 (2.27.1) the library added an executable utility that allows converting ANSI formatting and ANSI Art files:

npm install -g jquery.terminal
LC_ALL=C MAN_KEEP_FORMATTING=1 man -P cat man | from-ansi > man.txt

This will save the output of the man command and save it into a file (-P will use cat as pager and the env variables will make sure that you will get formatting and English version of the man page).

If you have ANSI art file (you can get those from 16colo.rs). You can generate jQuery Terminal formatting file with this command:

from-ansi -i file.ans -o file.jqt -a

to see help use:

from-ansi --help

or

from-ansi

NOTE: when using anything ANSI related you should use ansi option set to true:

term.echo('<ANSI>', {
  ansi: true
});

it will remove the empty space between lines when ANSI art is used. The code just add ansi class in CSS to containing div. This is need for instance when you run figlet with any ANSI font.

Custom Syntax highlighting

You can also create your own formatters (this is how terminal's prism.js works). The low-level mechanism for formatters consists of arrays:

$.terminal.defaults.formatters

which can be arrays of functions or arrays with regular expressions and strings.

In functions, you can do any text replacement and return strings with the specific Terminal formatting markup.

To add a new formatter, you can push value to this array, overwrite it with a new array or use the helper function $.terminal.new_formatter. This function will inject your formatter, before any nested formatting formatter, that is the default formatter; it allows you to have nested formatting like in HTML [[;red;]foo [[;blue;]bar] baz].

If you want that your nested formatters inherit style example:

[[b;;]foo [[i;;]bar] baz] and you want that bar needs to inherit bold style, then you should use this code to enable that:

$.terminal.nested_formatting.__inherit__ = true;

See Inherit Demo. The __inherit__ was made the default in version 2.28.0.

If you use the new_formatter function, your formatter also can have nesting.

For instance, if you want the text hello to be red and the text world to be green, you can use this formatter:

$.terminal.new_formatter(function(string) {
    return string.replace(/hello/g, '[[;red;]hello]').replace(/world/g, '[[;green;]world]');
});

You can also put two arrays:

$.terminal.new_formatter([/hello/g, '[[;red;]hello]']);
$.terminal.new_formatter([/world/g, '[[;green;]world]']);

Note: you should use regular expressions with g to be sure to replace all instances of the string. If your string or regex is without g, it will replace only the first instance of the string.

Here is an example to add an underline formatter, that looks like this _hello world_ _this is underline_.

$.terminal.new_formatter([/_([^_]+)_/g, '[[u;;]$1]']);

Because formatters are in the $.terminal namespace, they are globals for the whole page, so you can't have two terminals on the page and each one have different formatters (yes you can have multiple terminals on one page).

This may change in the future, but it will be a breaking change so it could be in the next major version. If you're using a 1.x.x version, from unpkg.com, you will not be affected.

NOTE: if you need to create formatter that changes the length of the string (like replacing foo with hello world) then you need to use the special function $.terminal.tracking_replace. It will track the cursor position after replacement, which was mostly created by Stack Overflow user T.J. Crowder.

The cursor movement will be a little weird because the cursor will be at the beginning of the resulting string and then at the end of it while you move the virtual cursor on the original string. So you will not be able to remove any character in the input string.

To have a correct cursor position, without the above limitations, you need to use the function like this:

$.terminal.new_formatter(function(string, position) {
    return $.terminal.tracking_replace(string, /hello/g, '[[;red;]hello world]', position);
});

This will be handled for you if you use a simple array. But this is the only option if you need to put some kind of logic in the replacement.

The following code will be roughly the same as above:

$.terminal.new_formatter([/hello/g, '[[;red;]hello world]']);

Note: one more limitation of formatters is that they are executed on strings between formatting so the above formatter will not create an infinite loop of replacement. With the exception of formatter like $.terminal.nested_formatting which is always added to the list when including the library.

If you want to have formatters based on interpreters, like for instance, a mysql command that have SQL syntax and a js command to have javascript formatting, etc... then you can create a stack of formatters (stack is the kind of the data structure more on Wikipedia). An example of how to do that is in example section on a website.

Another useful formatted is upperCase formatter:

$.terminal.new_formatter(str => str.toUpperCase());

Everything on the terminal will be uppercase (except default greetings that is not affected by formatters). See CodePen demo.

Attribute formatter

jQuery terminal formatting also supports setting specific attributes using JSON format. Below is a formatting that allows printing big font text:

$.terminal.defaults.allowedAttributes.push('style');

$.terminal.new_formatter([
    /<big>(.*?)<\/big>/g, '[[;;;;;{"style": "--size: 1.5;letter-spacing: 2px"}]$1]'
]);

And this allows using handy XML like format:

term.echo('<big>Hello World</big>');

Of course you can use formatting directly:

term.echo('[[;;;;;{"style": "--size: 1.5;letter-spacing: 2px"}]Hello World]');

But using your own formatting is much cleaner and your code looks much better. Especially if you will use this syntax a lot.

By default safe attributes are only ['title', 'target', 'rel', /^aria-/, 'id', /^data-/], you can use explicit string as value. Or using regular expressions.

NOTE By using attribute in links, you will remove any default attributes added like rel and target. So if you want them to be preserved you need to add them yourself.

Markdown links

Here is an example of creating markdown links. It doesn't work exactly as in a real markdown parser and may break when using parentheses or brackets but it works most of the time

$.terminal.new_formatter([/\[([^]+)\]\((.*?)\)/g, function(_, label, url) {
    return `[[!;;;;${url}]${label}]`;
}]);

After adding this you will be able to use links like on GitHub:

[jQuery Terminal](https://terminal.jcubic.pl/)

If you want to go further with Markdown, check Peggy Grammar for simple Markdown.

Image XML formatter

Another example can be links and images:

$.terminal.new_formatter([
    /<img>(.*?)<\/img>/g, '[[@;;;;$1]]'
]);

term.echo('<img>https://placekitten.com/200/287</img>');

You can also use formatting that looks similar to HTML:

$.terminal.new_formatter([
    /<img src="([^"]+)"(?: alt="([^"]+)")?\/?>/g, '[[@;;;;$1]$2]'
]);

term.echo('<img src="https://placekitten.com/200/287" alt="This is kitten"/>');

Link XML Formatter

Another handy formatter is formatting for creating links:

$.terminal.new_formatter([
    /<a href="([^"]+)">([^>]+)<\/a>/g, '[[!;;;;$1]$2]'
]);

term.echo('<a href="https://terminal.jcubic.pl">jQuery Terminal</a>')

NOTE: if you want to have formatted that will allow you to combine formatters (example big, img and a) the best way is to create a single formatter that will parse this XML like format. Look at XML formatter as example xml_formatting.js, that allow to use XML syntax for colors.

Extension XML Formatter

XML formatting is defined in xml_formatting.js file. You can import it using CDN or any other method described in Getting Started Guide.

The main feature adds each CSS color as its own XML tag. So you can use e.g.: <red>this is an error</red> to make the text red.

In version 2.28.0 xml_formatter was updated to allow to use of better XML-like syntax. Available tags (except CSS colors) are:

<img src="<URL>" alt="text"/>
<bold>text</bold>
<overline>text</overline>
<strike>text</strike>
<underline>text</underline>
<glow>text</glow>
<italic>text</italic>
<link href="<URL>">text</link>

There are also aliases: b for bold, i for italic, a for a link.

In version 2.29.0 those two tags got removed:

<large size="<num>">text</large>
<wide spacing="<num>.px">text</wide>

and replaced with single:

<font size="<num>" spacing="<num>.px">text</font>

Using XML formatter is much better because it supports nesting like normal XML syntax. And you don't need to worry about XSS and escaping user-generated content.

See Basic XML formatter demo.

Adding custom xml tags

in version 2.29.0 xml_formatter supports adding custom tags to the syntax. You can define new XML tags by adding them to object:

$.terminal.xml_formatter.tags

See an example of adding <figlet> as new XML syntax that can be used with font tag that changes the size of ASCII Art and color tags.

Overwrite colors

By add new tag that is the same as given color you can change the default clor:

Here is example of adding tag blue:

$.terminal.xml_formatter.tags.blue = (attrs) => {
   const cls = attrs.class ?? '';
   return '[[;' + #55f + ';' + cls + ']';
};

This will alow to use syntax like this:

term.echo('<blue class="command">ls</blue>');

NOTE from version 2.40.0 you can also use any attribute with xml tags, and you no longer have default attributes like with low level formatting:

<a href="#about" class="command">about</a>

This creates HTML a tag with URL fragment and a given class. You no longer need to worry about target="_blank" that open link in a new tab.

Markdown parser

Here is Peggy grammar for creating a simple Markdown parser for jQuery Terminal:

start = arr:(header / content / [\n])+ {
	return arr.join('');
}

header = '#'+ ' '* text:inline_text* [\n] {
	return '[[b;;]' + text.join('') + ']';
}

list_item = _ '*' text:inline_text* [\n] {
  return '*' + text.join('') + '\n';
}

inline_text = bold / italic / link / [^\n]

content = text:(image / inline_text / list_item)+ nl:[\n]? { return text.join('') + (nl || ''); }

image = '!' + link:inline_link {
	return '[[@;;;;' + link.url + ']' + link.text + ']';
}

link = link:inline_link {
	return '[[!;;;;' + link.url + ']' + link.text + ']';
}

inline_link = '[' text:[^\]]* ']' '(' url:[^)]+ ')' {
	return {
    	text: text.join(''),
        url: url.join('')
    };
}

bold = bold_a / bold_b

bold_a = '__' text:(text_bold_a / italic) '__' {
	return '[[b;;]' + text + ']';
}
text_bold_a = text:[^_]+ {
	return text.join('');
}

bold_b = '**' text:(text_bold_b / italic) '**' {
	return '[[b;;]' + text + ']';
}

text_bold_b = text:[^\*]+ {
	return text.join('');
}

italic = italic_a / italic_b

italic_a = '*' text:[^\*]+ '*' {
	return '[[i;;]' + text.join('') + ']';
}

italic_b = '_' text:[^_]+ '_' {
	return '[[i;;]' + text.join('') + ']';
}

_ "whitespace" = [\s\t]*

The simple way to test this grammar is to copy and paste this grammar into Peggy Plaground or you can also check this CodePen demo.

Formatting targets

In version 2.18.0 jQuery Terminal allows picking where formatters should be applied:

There are 4 formatting targets echo command and prompt (and new animation). By default, formatters is applied everywhere but you can control that.

When using regex formatter you can control where formatter should be used:

$.terminal.new_formatter([
    /<img src="([^"]+)"(?: alt="([^"]+)")?\/?>/g, '[[@;;;;$1]$2]', { echo: true }
]);

If an object is not set it will apply formatting everywhere, but if you pass the object you need to specify which targets you want to support. The above formatter for images will only work in echo.

With the function the target is passed as an option parameter:

$.terminal.new_formatter(function(string, options) {
   if (options.echo) {
      return $.terminal.tracking_replace(string, /foo/, 'bar');
   }
   return string;
});

Limiting formetter to echo can be useful when you don't want users to type formatting (e.g. XML tags and format the code that is displayed on the terminal), but you may want formatters to be applied everywhere like with syntax highlighting where you want formatters to be in Command line and when you echo the text after you press enter. With formatters target, you have control over where you want your formatters to work.

Using JSX in jQuery Terminal

With help from vhtml project (and a stringjsx fork) that renders JSX to string you can use all the power of JSX and don't need to write strings in order to format the output.

Here is an example of the code (link to demo below):

/** @jsx vhtml */
/** @jsxFrag vhtml.Fragment */

const term = $('body').terminal({
    echo(message) {
        this.echo(<white>{ message }</white>);
    }
}, {
    greetings: <Greetings/>
});

function Greetings() {
    const url = 'https://terminal.jcubic.pl';
    return (
        <glow>
            <lime>
                <italic>
                    <a href={url}>jQuery Terminal</a>
                    {' '}
                    JSX
                </italic>
            </lime>
            {'\n'}
            test <white>echo</white> command
        </glow>
    );
}

Here is a demo on CodePen (with more code).

Meta property

Normally when you apply formatters, like Upper Case in one of the previous examples, formatters are applied only for text between formatters, because it can get messy when you try to operate over formatters. But if you write formatter that modify other formatters or just something that don't affect formatters like Upper Case formatter you can use __meta__ property set to true.

Example:

const upper = str => str.toUpperCase()
upper.__meta__ = true;
$.terminal.new_formatter(upper);

term.echo('[[;white;]jQuery Terminal] is flexible');

This will print white text "JQUERY TERMINAL" and default "is flexible". Note that the white name will also be uppercase, this doesn't matter in this case because CSS is case insensitive, but it may be problematic when dealing with more complex formatters.