• このエントリーをはてなブックマークに追加

ss-1335401781

はじめに

WordPressのプラグイン WP-Syntax で利用されていることでもおなじみの,シンタックスハイライトなPHPライブラリ GeSHi ですが,Textileにおける bc. とか bc.. とかの記法を少し拡張してシンタックスハイライトするのに使えないかどうか,ということを考えていました.

少々力技ではありますが,ざっと形になったので,Text::Textile::Pluggable::Plugin::SyntaxHighlight::GeSHi という名前をつけてGitHubに公開してみました.

できること

Textile記法では,コードブロックを表記するのに,bc. とか bc.. とかを使います.

bc. foo
bar
baz
 
this line is not code
 
bc.. foo
bar
baz
 
this line is code too

そこで,本プラグインでは,bc[lang].bc[lang].. という記法が使えるようにしてみました.で,lang が示す言語に沿ったシンタックスハイライトが施されるようになっています.(もちろん,GeSHiに対応しているもののみですが!)

bc[lang]. foo
bar
baz
 
this line is not code
 
bc[lang].. foo
bar
baz
 
this line is code too

使い方

use Text::Textile qw/textile/;
 
my $text = << '...';
bc[perl].
use strict;
use warnings;
print 'foobar.';
...
 
# procedural usage
my $html = textile($text, [qw/SyntaxHighlight::GeSHi/]);
 
# OOP usage
my $textile = Text::Textile::Pluggable->new(
    plugins => [qw/SyntaxHighlight::GeSHi/],
);
my $html = $textile->process($text);

環境的な前提

環境的には,次の3点が前提となります:

  • Text-Textile-Pluggable-0.03
  • PHP
  • GeSHi

Text-Textile-Pluggable-0.03

プラグインモジュールのロード処理まわりを少し修正したバージョンが必要になります.

PHP

PHPも使える必要があります.バージョンはそれなりにテキトーで問題ないかと思います.

GeSHi

これが肝心ですね.

ダウンロードは次から行えます:

<?php include_once 'geshi/geshi.php'; ?> ができるように,展開したものをそのまま include_path へ移動します.

なお,include_path は,次のようにすれば見つけられるでしょう:

% echo '<? phpinfo() ?>' | php | grep include_path
include_path => .:/Users/issm/local/lib/php => .:/Users/issm/local/lib/php

私の環境では,/Users/issm/local/lib/php がそれにあたるので,/Users/issm/local/lib/php/geshi/geshi.php として参照できるような形になればOK,という感じです.

力技の中身

  1. bc[lang].<pre><code language="lang">...</code></pre> というようなHTMLに変換される)
  2. 渡されたHTMLテキストから <pre><code language="lang">...</code></pre> となっている個所を抜き出す
  3. 抜き出したコード部分を一時ファイルに書き出す
  4. `echo $php_code | php -- $file $lang` みたいな感じでPHPを実行する
  5. PHP内では,一時ファイルを読んでGeSHiに通してできたHTMLを標準出力する,というようなことをしている
  6. 先の <pre><code language="lang">...</code></pre> な部分を,得られたHTMLで置換する
  7. これを繰り返す

PHPコードを標準入力として読み込ませれば,それ専用のスクリプトファイルを準備しなくてもいいよね,といった判断でこうなりました.

また,GeSHiを通すためのコード自体も引数とかで与えようとしたけれど,クオートとかの処理がよくわからなかったので,一時ファイルを経由する形にしてしまいました.

プラグインのソースコード

package Text::Textile::Pluggable::Plugin::SyntaxHighlight::GeSHi;
use 5.008001;
use strict;
use warnings;
use File::Temp qw/tempfile/;
use HTML::Entities qw/decode_entities/;
use Encode qw/encode_utf8 decode_utf8/;
our $VERSION = '0.01';
 
my $php_code = << '...';
<?php
include_once "geshi/geshi.php";
 
$file = $argv[1];
$lang = $argv[2];
$code = file_get_contents($file);
 
$geshi = new GeSHi($code, $lang);
$html = $geshi->parse_code();
 
echo $html;
?>
...
 
my $re_html_code = qr{<pre><code (?:\s language="([^\"]+)" )?>((?:.|\n)*?)</code></ pre>}mx;
# "</ pre>": WP-Syntaxが誤反応しないための対策.通常はスペースはない
 
sub init {
    my $o = shift;
    $o->{'plugin.geshi'} = {
        echo => '/bin/echo',
        php  => '/usr/bin/env php',
    };
}
 
sub post {
    my $o    = shift;
    my $text = shift;
 
    my $echo = $o->{'plugin.geshi'}{echo};
    my $php  = $o->{'plugin.geshi'}{php};
 
    while ( my @m = $text =~ $re_html_code ) {
        my ($lang, $code) = @m;
        $lang ||= 'text';
        chomp($code = decode_entities($code));
 
        my ($fh, $file) = tempfile(UNLINK => 1);
        print $fh encode_utf8($code);
        close $fh;
 
        $text =~ s{$re_html_code}{
            decode_utf8( `$echo '$php_code' | $php -- $file $lang` );
        }ex;
    }
 
    return $text;
}

おわりに

以上,Textile記法をちょこっと拡張してGeSHiを使ったシンタックスハイライトを行えるようにするための Text::Textile::Pluggable プラグインを書いてみたお話でした.

なお,冒頭のスクリーンショットは,SiTeWiki に本プラグインを追加してみたときのものです.一応動いていますねw