Display beautiful Perl code in HTML without JavaScript

Would you like to display beautiful syntax-highlighted Perl code on the web without using JavaScript? Maybe you’d like to use an existing CSS markup theme without having to write in-line CSS in your Perl code? If yes, take a look at PPI::Prettify.

Background

The prettify.js library does a wonderful job of robustly syntax coloring a large number of different languages for displaying code on the web. It’s used on blogs.perl.org; we use it on PerlTricks.com. But because Perl is an ambiguous language, prettify.js often doesn’t tokenize all of the code correctly. What’s worse is if a user has JavaScript disabled, the code will not be highlighted at all. That’s why I wrote PPI::Prettify. It runs in the backend using PPI::Document so it’s faster more accurate than prettify.js, but outputs the same HTML codes as prettify.js does, enabling you to re-use any of the existing CSS themes available (here, here and here for example).

Requirements

You’ll need PPI::Prettify and can install it via CPAN at the terminal:

$ cpan PPI::Prettify

In terms of OS compatibility, PPI::Prettify is pure-Perl so you should be able to run it on any platform that has Perl installed.

Tokenizing inline Perl code

PPI::Prettify exports a prettify() method that takes a string of Perl code, and returns it tokenized with <span> tags. To be safe, PPI::Prettify employs HTML encoding on all token content. Let’s whip up a quick script to demo prettify():

use warnings;
use strict;
use PPI::Prettify;

read(main::DATA, my $code, 500);

print prettify({ code => $code });

__DATA__
# a simple OO class

package Shape;

sub new {
    my ($class, $args) = @_;
    my $self = {
        color  => $args->{color} || 'black',
        length => $args->{length} || 1,
        width  => $args->{width} || 1,
    };
    return bless $self, $class;
}

sub get_area {
    my $self = shift;
    return $self->{length} * $self->{width};
}

sub get_color {
    my $self = shift;
    return $self->{color};
}

sub set_color {
    my ($self, $color) = @_;
    $self->{color} = $color;
}

1;

The script uses the __DATA__ token to create a filehandle to some inline Perl code (The code is a simple OO example taken from our article Old School Object Oriented Perl). The read function slurps the filehandle contents into $code. We then use the prettify() function to tokenize and markup the Perl code.

Running that script returns the Perl code surrounded by <span> tags. This is a summary of the markup produced by prettify():

<pre class="prettyprint"><span class="com"># a simple OO class
</span><span class="pln">
</span><span class="kwd">package</span><span class="pln"> </span><span class="atn">Shape</span><span class="pln">;</span>
...
</pre>

The example below shows how the markup looks in HTML (using the desert CSS theme).

# a simple OO class

package Shape;

sub new {
    my ($class, $args) = @_;
    my $self = {
        color  => $args->{color} || 'black',
        length => $args->{length} || 1,
        width  => $args->{width} || 1,
    };
    return bless $self, $class;
}

sub get_area {
    my $self = shift;
    return $self->{length} * $self->{width};
}

sub get_color {
    my $self = shift;
    return $self->{color};
}

sub set_color {
    my ($self, $color) = @_;
    $self->{color} = $color;
}

1;

Two things to note here: disabling JavaScript will have no effect on the syntax highlighting above, as it’s generated in backend using PPI::Prettify. Second, the code displays multiline comments correctly, (everything after __DATA__) unlike prettify.js.

Tokenizing Perl code stored in a file

It’s easy to prettify existing Perl code from a file. You can do this in one line of Perl at the terminal:

$ perl -MPPI::Prettify -MFile::Slurp -e '$code=read_file("output");print prettify({code=>$code})'

Advanced feature 1: debug mode

The prettify() method also takes an optional debug parameter:

my $html = prettify({ code => $code, debug => 1 });

Debug mode will provide the same output, however every tag will be given a “title” attribute with the original PPI::Token class as the value. This can help you to understand how the original PPI::Token class maps to the markup by hovering the cursor over the text. The code from earlier has been printed with debug mode turned on. Try hovering!

# a simple OO class

package Shape;

sub new {
    my ($class, $args) = @_;
    my $self = {
        color  => $args->{color} || 'black',
        length => $args->{length} || 1,
        width  => $args->{width} || 1,
    };
    return bless $self, $class;
}

sub get_area {
    my $self = shift;
    return $self->{length} * $self->{width};
}

sub get_color {
    my $self = shift;
    return $self->{color};
}

sub set_color {
    my ($self, $color) = @_;
    $self->{color} = $color;
}

1;

Advanced feature 2: override the mapping

You may want to change how certain tokens of Perl code are marked up. PPI::Prettify exports the mapping in a hashref, called $MARKUP_RULES. Every PPI::Token class is a key, with the value being the CSS class name that prettify.js uses (and the prettify CSS themes expect). For example PPI::Token::Comment is mapped to “com”:

'PPI::Token::Comment' => 'com'

Combined with debug mode, it should be straightforward to change the mapping of a particular PPI::Token class to the prettify class you require.

Alternatives

Consider using Adam Kennedy’s PPI::HTML if you are happy writing inline-CSS in your Perl code, or need more detailed markup than the 10 or so classes provided by PPI::Prettify. It’s a more mature module and can do line numbering too.


This article was originally posted on PerlTricks.com.

Tags

David Farrell

David is a professional programmer who regularly tweets and blogs about code and the art of programming.

Browse their articles

Feedback

Something wrong with this article? Help us out by opening an issue or pull request on GitHub