<?xml version="1.0" encoding="windows-1252"?>
<node id="314528" title="On-the-fly all-languages syntax highlighting" created="2003-12-13 09:08:43" updated="2005-08-13 10:57:12">
<type id="1042">
CUFP</type>
<author id="127116">
gmax</author>
<data>
<field name="doctext">
&lt;p&gt;Syntax highlighting has always been one of my favorite features in every
editor I have used. After becoming addicted to it, I have always regretted
not being able to share colored code with other people.&lt;/p&gt;
&lt;p&gt;If we talk about Perl code alone, there are a few available tools to
create colored code in HTML and publish it on the web.&lt;/p&gt;
&lt;p&gt;When you need to show code in more than one language, though, things become
more difficult. Think about presenting the installation and customization
of a complex system. You'd need to show Perl code, Apache configuration files,
HTML code, SQL queries, and perhaps some XML. &lt;/p&gt;
&lt;p&gt;When you publish this code on the web, what was clearly highlighted and easily
understandable in your editor screen becomes a flat sequence of black on white text.&lt;/p&gt;

&lt;h3&gt;Introducing Text::VimColor&lt;/h3&gt;
&lt;p&gt;Two wonderful features of &lt;a href="http://www.vim.org"&gt;Vim&lt;/a&gt; are its ability
of highlighting different languages (384 as of today) and producing an HTML page
with the same layout of the code on screen.&lt;/p&gt;
&lt;p&gt;(And don't forget Vim's ability to highlight nested syntax, such as [id://270014|Perl embedded in HTML] or &lt;a href="http://jeffa.perlmonk.org/vi/example.html"&gt;SQL embedded in Perl&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Producing code manually with vim is not user friendly and it is quite slow.
If you need to publish code on a regular basis, producing HTML pages manually from
Vim is a hassle.&lt;/p&gt;
&lt;p&gt;Enter Geoff Richards' [http://search.cpan.org/perldoc?Text::VimColor|Text::VimColor], a module
that removes your need to remember difficult commands and to cut and paste your
code snippets.&lt;/p&gt;

&lt;readmore&gt;

&lt;p&gt;Given these code fragments:&lt;/p&gt;
&lt;code&gt;
# CodeSamples.pm
package CodeSamples;

our $ctext = &lt;&lt;'CTEXT';
#include &lt;stdio.h&gt;
int main()
{
    printf("hello world\n");
    return 0;
}
CTEXT

our $perltext = &lt;&lt;'PTEXT';
my $query = qq{SELECT mycol, COUNT(*)
    FROM mytable WHERE mycol &lt;= 10 GROUP BY mycol};
print $$_,$/ for @{ $dbh-&gt;selectcol_arrayref($query) };
# Notice that $query has nested SQL syntax
PTEXT
1;
&lt;/code&gt;

&lt;p&gt;This script will produce nicely highlighted code (HTML + CSS).&lt;/p&gt;

&lt;code&gt;
#!/usr/bin/perl -w
# test_vimcolor.pl
use strict;
use CGI qw/:standard/;
use Text::VimColor;
use CodeSamples;   # contains code samples in C and Perl

my $csyntax = Text::VimColor-&gt;new(
    string   =&gt; $CodeSamples::ctext,
    filetype =&gt; 'c'
)or die("can't create C object ($!)\n");

my $perlsyntax = Text::VimColor-&gt;new(
    string   =&gt; $CodeSamples::perltext,
    filetype =&gt; 'perl'
)or die("can't create perl object ($!)\n");

my $fperlsyntax = Text::VimColor-&gt;new(
    file     =&gt; $0,
    filetype =&gt; 'perl'
)or die("can't create perl object ($!)\n");

print
    start_html(-title=&gt;"Text::VimColor test",
               -style=&gt;{'src'=&gt;'light.css'}
              ),
    h2("C"),
    pre( $csyntax-&gt;html),
    hr,
    h2("Perl"),
    pre( $perlsyntax-&gt;html),
    hr,
    h2("Perl (file)"),
    pre( $fperlsyntax-&gt;html),
    hr,
    h2("CSS"),
    pre(Text::VimColor-&gt;new(file =&gt;'light.css', filetype =&gt; 'css')-&gt;html),
    hr,
    h2("Perl (package)"),
    pre(Text::VimColor-&gt;new(file =&gt;'CodeSamples.pm',  filetype =&gt; 'perl')-&gt;html),
    hr,
    h2("Perl (another package)"),
    pre(Text::VimColor-&gt;new(file =&gt;'VimColorCache.pm',  filetype =&gt; 'perl')-&gt;html);
&lt;/code&gt;
&lt;p&gt;See &lt;a href="http://gmax.oltrelinux.com/test_vimcolor.html"&gt;the colorful result&lt;/a&gt;.&lt;/p&gt;

&lt;/readmore&gt;

&lt;h3&gt;Highlighting systems overview&lt;/h3&gt;

&lt;p&gt;Before continuing, let me show the alternatives. I have tried all of them, and
I have good and bad feelings for each one of them. I am currently in favor of
[CPAN://Text::VimColor] for the reason given before, and more.&lt;/p&gt;

&lt;table border="1" cellpadding="3" cellspacing="0"&gt;
&lt;tr&gt; &lt;th&gt;Application&lt;/th&gt; &lt;th&gt;Pro&lt;/th&gt; &lt;th&gt;Con&lt;/th&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;[http://search.cpan.org/search?query=perltidy&amp;mode=all|perltidy]&lt;/td&gt; &lt;td&gt;Fast and accurate&lt;/td&gt; &lt;td&gt; Only Perl&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;&lt;a href="http://www.gnu.org/software/src-highlite/source-highlight.html"&gt;GNU source highlight&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Very fast&lt;/td&gt; &lt;td&gt;Hard to customize. &lt;br&gt;Only a few languages&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;[CPAN://Syntax::Highlight::Perl]&lt;/td&gt; &lt;td&gt;Fast and customizable&lt;/td&gt; &lt;td&gt;Only Perl&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;[CPAN://Text::VimColor]&lt;/td&gt; &lt;td&gt;All languages.&lt;br&gt; Easily customizable&lt;/td&gt; &lt;td&gt;Slower than other modules.&lt;br&gt; Works only on Unix (as of today)&lt;/td&gt; &lt;/tr&gt;
&lt;/table&gt;

&lt;h3&gt;Improving performance&lt;/h3&gt;
&lt;p&gt;As I said, [CPAN://Text::VimColor] main deficiency is its poor performance compared to other
modules. Although the latest version (0.07) is twice as fast as the previous one, it is still
way too slow for any sensible web usage.&lt;/p&gt;
&lt;p&gt;Therefore, I decided to create a caching object, to improve Text::VimColor basic performance.&lt;/p&gt;

&lt;readmore&gt;
&lt;p&gt;The simplest way I could think of was a tied hash with [CPAN://DB_File]. I have also simplified
the object interface, to make it easier to use.&lt;/p&gt;

&lt;code&gt;
package VimColorCache;
use strict;
use warnings;
use Text::VimColor;
use Digest::MD5 qw/md5_hex/;
use DB_File;

our $VERSION = '0.1';

sub new {
    my $class = shift;
    my $filename = shift || 'VimColorCache.db';
    my %code_items;
    tie %code_items, 'DB_File', $filename
        or return undef;
    my $self = bless {
        code_items =&gt; \%code_items
    }, $class;
    return $self;
}

sub _get_text {
    my $filename = shift;
    my $text = undef;
    open IN, $filename or return undef;
    local $/;
    $text = &lt;IN&gt;;
    close IN;
    return $text;
}

sub draw {
    my $self  = shift;
    my $text  = shift;        # either the code or the file name
    my $input = shift;        # file or string
    my $syntax_type  = shift; # syntax type (perl, c, sql, html, xml, etc)
    my $output  = shift;      # output mode
    return undef unless $output =~ /^(?:html|xml)$/;
    return undef unless $input =~ /^(?:file|string)$/;
    my $code = $text;
    if ($input eq 'file') {
        $code = _get_text($text) or return undef;
    }
    $code =~ s/\t/    /g; # turns tabs into 4 spaces
    my $signature = md5_hex($code);
    if (exists $self-&gt;{code_items}-&gt;{$output.$signature}) {
        return $self-&gt;{code_items}-&gt;{$output.$signature}
    }
    else {
        my $syntax = Text::VimColor-&gt;new (
            $input =&gt; $text,
            filetype =&gt; $syntax_type
        ) or return $code;
        my $out = $syntax-&gt;$output;
        $self-&gt;{code_items}-&gt;{$output.$signature} = $out;
        return $out;
    }
}

sub remove {
    my $self   = shift;
    my $text   = shift;  # either the code or the file name
    my $input  = shift;  # file or string
    my $output = shift;
    my $code = $text;
    if ($input eq 'file') {
        $code = _get_text($text) or return undef;
    }
    my $signature = md5_hex($code);
    delete $self-&gt;{code_items}-&gt;{$output.$signature};
}

1;
__END__

=head1 NAME

VimColorCache - caches the result of Text::VimColor

=head1 SYNOPSIS

  use VimColorCache;

  my $filename = 'syntax.db';

  my $vcc = VimColorCache-&gt;new($filename);

  print $vcc-&gt;draw('print $_,$/ unless m/^\s*$/g',
                   'string', 'perl', 'html');

  print $vcc-&gt;draw('hello.c', 'file', 'c', 'html';

=head1 class methods

=over 4

=item new()

The constructor accepts an optional filename where to store previously
highlighted code snippets.
The default file name is VimColorCache.db

=item draw()

Returns a properly highlighted text.

It needs some parameters:

    - text          either a filename or a string containing
                    code to be formatted
    - input         'file' or 'string'
    - syntax_type   language to use (perl, c, php, xml, SQL)
                    The same as Vim's 'filetype'
    - output        either 'html' or 'xml'

If the code passed as string or file has already been processed, then
the corresponding formatted text is returned, otherwise a Text::VimColor
object is created and the highlighted syntax is processed from scratch.

    print $vcc-&gt;draw('hello.c','file', 'c', 'html');

    open IN, "hello.c" or die "can't open\n";
    my $c_text = do { local $/; &lt;IN&gt; };
    close IN;

    print $vcc-&gt;draw($c_text,'string', 'c', 'html');

These two instructions will print the same output.
Only, the first one will be slow, the second one will be
extremely fast.

=item remove()

Removes an item from the repository.

It needs the same parameters as draw(), except "syntax_type"

     $vcc-&gt;remove('hello.c','file', 'html');

=back

=head1 AUTHOR

Giuseppe Maxia, a.k.a. gmax
(gmax_at_cpan.org)

=head1 COPYRIGHT

Same as Perl itself.

=cut
&lt;/code&gt;

&lt;p&gt;VimColorCache is a layer between the application and the highlighting module.
It works on the assumption that, in most cases, code is published once and shown
many times. Sometimes it is modified, but mostly it is just published and then left
on the page for public consumption. In this scenario, the first poster has to
wait one or two seconds for the highlighting engine to do its job, but every
further request of the same code is resolved instantly.&lt;/p&gt;
&lt;p&gt;The first example shown in this node could be rewritten using VimColorCache
as follows:&lt;/p&gt;

&lt;code&gt;
#!/usr/bin/perl -w
# test_vimcolor_cache.pl
use strict;
use CGI qw/:standard/;
use VimColorCache;
use CodeSamples;

my $vcc = VimColorCache-&gt;new or die("can't create object ($!)\n");
print
    start_html(-title=&gt;"VimColorcache test",
               -style=&gt;{'src'=&gt;'light.css'}
              ),
    h2("C"),
    pre( $vcc-&gt;draw($CodeSamples::ctext, 'string', 'c', 'html')),
    hr,
    h2("Perl"),
    pre($vcc-&gt;draw($CodeSamples::perltext, 'string', 'perl', 'html')),
    hr,
    h2("Perl (file)"),
    pre($vcc-&gt;draw($0, 'file', 'perl', 'html')),
    hr,
    h2("CSS"),
    pre($vcc-&gt;draw('light.css', 'file', 'css', 'html')),
    hr,
    h2("Perl (package)"),
    pre($vcc-&gt;draw('CodeSamples.pm', 'file', 'perl', 'html')),
    hr,
    h2("Perl (another package)"),
    pre($vcc-&gt;draw('VimColorCache.pm', 'file', 'perl', 'html'));
&lt;/code&gt;

&lt;p&gt;And &lt;a href="http://gmax.oltrelinux.com/test_vimcolor_cache.html"&gt;the second colorful result&lt;/a&gt;
shows exactly the same output as the previous one, except for the page title.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;update (1)&lt;/b&gt;&lt;br&gt;
&lt;u&gt;CAVEAT&lt;/u&gt;. If you pass a file to [CPAN://Text::VimColor] and at the same time you are editing the same file with Vim, it will return an error. You should either ensure that your file is not currently in use by Vim before passing it to the class constructor, or slurp it into a scalar and pass it as a string.
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update (2)&lt;/b&gt;&lt;br&gt;
In case you are wondering just how slow is Text::VimColor without a cache, here is an example.&lt;br&gt;
Processing times are acceptable for small scripts, but become unbearable for large ones.&lt;/p&gt;
&lt;table border=1 cellpadding=3 cellspacing=0&gt;
    &lt;tr&gt;
        &lt;th rowspan="2"&gt;Application&lt;/th&gt;
        &lt;th colspan="4"&gt;Time to highlight&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;th&gt;strict.pm&lt;br&gt; (2.6 KB)&lt;/th&gt;
        &lt;th&gt;Benchmark.pm&lt;br&gt; (22 KB)&lt;/th&gt;
        &lt;th&gt;CGI.pm&lt;br&gt; (221 KB)&lt;/th&gt;
        &lt;th&gt;DBI.pm&lt;br&gt; (226 KB)&lt;/th&gt;
    &lt;/tr&gt;
   &lt;tr&gt;
        &lt;td&gt;perltidy&lt;/td&gt;
        &lt;td align="right"&gt;0.30&lt;/td&gt;
        &lt;td align="right"&gt;0.86&lt;/td&gt;
        &lt;td align="right"&gt;1.76&lt;/td&gt;
        &lt;td align="right"&gt;2.77&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;source-highlight&lt;/td&gt;
        &lt;td align="right"&gt;0.00&lt;/td&gt;
        &lt;td align="right"&gt;0.03&lt;/td&gt;
        &lt;td align="right"&gt;0.19&lt;/td&gt;
        &lt;td align="right"&gt;0.21&lt;/td&gt;
    &lt;/tr&gt;
     &lt;tr&gt;
        &lt;td&gt;Text::VimColor&lt;/td&gt;
        &lt;td align="right"&gt;0.34&lt;/td&gt;
        &lt;td align="right"&gt;2.35&lt;/td&gt;
        &lt;td align="right"&gt;16.62&lt;/td&gt;
        &lt;td align="right"&gt;17.23&lt;/td&gt;
    &lt;/tr&gt;
     &lt;tr&gt;
        &lt;td&gt;VimColorCache&lt;/td&gt;
        &lt;td align="right"&gt;0.01&lt;/td&gt;
        &lt;td align="right"&gt;0.01&lt;/td&gt;
        &lt;td align="right"&gt;0.01&lt;/td&gt;
        &lt;td align="right"&gt;0.02&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;


&lt;/readmore&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;


&lt;div class="pmsig"&gt;&lt;div class="pmsig-127116"&gt;
&lt;pre&gt;
 _  _ _  _  
(_|| | |(_|&gt;&lt;
 _|   
&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;</field>
</data>
</node>
