pukiwiki/texthighlight.inc.php

(1555d) 更新

公開メモ

pukiwiki 上でソースコードをカラー表示するためのプラグイン

http://shjs.sourceforge.net/index.html にある SHJS という ライブラリを使って JavaScript で色を付けることができます。

pukiwiki 文法ではソースコードなどを記述するには文頭に空白を 置きます。

たとえば、こんな感じですね。

printf("Hello, world!\n");

このプラグインを入れて、ソースコードブロックの一行目に、" LANG:C++" と書くと、

LANG:C++
printf("Hello, world!\n");

となります。

また、" LANG:C++(linenumber)" とすると、左側に行番号を付ける事もできます。
行番号を付けた場合にも、ソースコード部分のみを選択&コピーできるようになっています。

LANG:C++(linenumber)
printf("Hello, world!\n");
printf("Hello, world!\n");
printf("Hello, world!\n");

下記の php ソースであれば " LANG:php" と書いてあります。

SHJS は C++, php の他にも、下記の多くの言語に対応していて、 この機能を pukiwiki 上でそのまま使う事ができます。

BisonJava properties filesPHP
CJavaScriptProlog
C++JavaScript with DOMPython
C#LaTeXRPM spec files
ChangeLogLDAP filesRuby
CSSLog filesS-Lang
Desktop filesLSM (Linux Software Map) filesScala
DiffM4Shell
FlexMakefileSQL
GLSLObjective CamlStandard ML
HaxeOracle SQLTcl
HTMLPascalXML
JavaPerlXorg configuration files

あるいは、文法ファイルを頑張って手で書けば、 その他の言語にも割と簡単に対応させることができます。

役に立ちそうだったらご利用ください。

ソースコード

LANG:php(linenumber)
<?php
/*
 *  シンタックスハイライトプラグイン - texthighlight 2.0
 * 
 * プログラムコードなどをシンタックスハイライト (+行番号付け) して
 * 表示するプラグイン。内部で shjs というライブラリを使っています。
 * 
 * by Osamu TAKEUCHI <osamu@big.jp> http://dora.bk.tsukuba.ac.jp/~takeuchi/
 * 
 * 利用・配布・改変は自由に行ってください。
 * ただし、結果について作者は保証しません。
 * 各自の責任で行うようにしてください。
 * 
 * 
 * 使い方:
 * 
 * インストールはまず pukiwiki の plugin フォルダに texthighlight.inc.php 
 * を放り込む。
 * 
 * http://shjs.sourceforge.net/index.html から shjs を落としてきて、
 * pukiwiki フォルダの直下(plugin フォルダと同じ階層)に shjs という
 * 名前のフォルダを掘ってそこに展開する。
 * 
 * このプログラムは実際にはプラグインではなくフィルタのようなものなので、
 * plugin フォルダに入れるだけでは動かない。
 *
 * スキンファイル (skin/pukiwiki.skin.php など) を以下の要領で変更する。
 *
 * return $body; となっている部分を return texthighlight($body); とする。
 * その直前に require_once("plugin/texthighlight.inc.php"); も必要。
 *
 * ヘッダ部分で shjs ライブラリへのリンクを張る
 *   <link rel="stylesheet" type="text/css" href="shjs/css/sh_ide-eclipse.css" />
 *   <script type="text/javascript" src="shjs/sh_main.js"></script>
 * 
 * body onLoad で shjs を呼び出す
 *   <body onLoad="sh_highlightDocument('shjs/lang/', '.js');">
 *
 */

function texthighlight($body)
{
    // 本当は以下のように preg_replace_callback 一発で終わらせたいの
    // だけれども、これだと $body が長くなるとうまくいかないのでちまちまと

//  return preg_replace_callback(
//      "#<pre>(?:LANG(?:UAGE)?:".
//            "([a-zA-Z0-9\#\+\-]+)(\(([^\)]*)\))?\n)?((\n|.)*?)</pre>#",
//      'do_texthighlight', 
//      $body);

    $newbody= "";
    while( trye ){
        $pos= stripos( $body, "<pre>LANG" );
        if($pos===FALSE)
            break;
        $newbody.= substr($body, 0, $pos);
        $body= substr($body, $pos);
        $end= stripos( $body, "</pre>" );
        $pre= substr($body, 0, $end);
        $body= substr($body, $end+6);
        if( preg_match("#<pre>\s*(?:LANG(?:UAGE)?:".
                "([a-zA-Z0-9\#\+\-]+)(\(([^\)]*)\))?\s*\n)?#", 
                $pre, $matches) ) {
            $newbody.= do_texthighlight($matches, substr($pre, strlen($matches[0])));
        }else{
            $newbody.= $pre;
        }
    }
    return $newbody . $body;
}

function do_texthighlight($matches, $source)
{
    $language= strtolower($matches[1]);
    $args= $matches[3] ? split(",", $matches[3]) : array();
    $source= html_entity_decode($source);

    // 別名に対応
    if($language == "c#") $language= "csharp";
    if($language == "c++") $language= "cpp";
    if($language == "s-lang") $language= "slang";

    if( in_array("linenumber", $args) ){
        // 行番号付け
        $n= count(split("\n", $source));
        $numbers= "";
        for($line=1; $line<=$n; $line++)
            $numbers .= sprintf("%d:\n", $line);
        return '<table class="sh_linenumber"><tr><td><pre class="sh_linenumber">' .
               $numbers . '</pre></td><td><pre class="sh_' . $language . '">' . 
               htmlspecialchars($source) . '</pre></td></tr></table>';
    }else
        if( $language ){
        // 横幅を制限するために table に入れている。
        return '<table class="sh_bound"><tr><td><pre class="sh_' . $language . '">' . 
               htmlspecialchars($source) . '</pre></td></tr></table>';
    }else{
        // 横幅を制限するために table に入れている。
        return '<table class="sh_bound"><tr><td><pre>' .
               htmlspecialchars($source) . '</pre></td></tr></table>';
    }
}

?>

スタイルシート(pukiwiki.css.php など) の末尾に追加する内容:

LANG:CSS(linenumber)
	/* texthighlight.inc.php 関連 */

pre {
  background-color:#f0f0ff !important;
  border: 1px #66e dashed;
  overflow:auto;
  margin:0px;
  padding:.5em;
}

table.sh_bound {
  min-width:40em;
  margin:0px;
  margin-right:2em;
  margin-left:0.5em;
}

pre.sh_console {
  background-color:black !important;
  color:white;
  border: 0px;
}

table.sh_linenumber tr td {
  margin:0px;
  padding:0px;
  border:0px;
  background-color:#f0f0ff;
}

table.sh_linenumber tr {
  margin:0px;
  padding:0px;
  border:0px;
}

pre.sh_linenumber {
  color:grey;
  text-align:right;
}

table.sh_linenumber tr td pre {
  border:0px;
  margin:0px;
  padding:.5em;
}

table.sh_linenumber {
  background-color:#f0f0ff;
  border: 1px #66e dashed;
  margin:0px;
  margin-right:2em;
  margin-left:0.5em;
}

Verilog 用の言語ファイル

sh_verilog.js

LANG:javascript(linenumber)
if (! this.sh_languages) {
  this.sh_languages = {};
}
sh_languages['verilog'] = [
    [
        [ /\/\/[ \t]*synthesis[ \t]+attribute[ \t]/gi, 'sh_preproc', 5 ],
        [ /^[ \t]*`\w/gi, 'sh_preproc', 5 ],
        [ /\#[\w._]+/g, 'sh_preproc', -1 ],
        [ /\/\//g, 'sh_comment', 1 ],
        [ /\/\*/g, 'sh_comment', 2 ],
        [ /\(\*\)/g, 'sh_symbol', -1 ],
        [ /\(\*/g, 'sh_preproc', 4 ],
        [ /\b\d+\b|(?:\b\d+)?'b[01xz]+\b|(?:\b\d+)?'h[0-9a-fxz]+\b|(?:\b\d+)?'d[\dxz]+\b/gi, 'sh_number', -1],
        [ /"/g, 'sh_string', 3 ],
        [ /\$\w+\b/g, 'sh_function', -1 ],
        [ /(?:\.|\$)\w+\s*(?=\()/g, 'sh_function', -1 ],
        [ /\b(?:shortint|int|longint|byte|bit|logic|reg|integer|time|signed|unsigned|real|shortreal|void|candle|event|string)\b/g, 'sh_type', -1 ],
        [ /\b(?:always|end|ifnone|or|rpmos|tranif1|and|endcase|initial|output|rtran|tri|assign|endmodule|inout|parameter|rtr|nif0|tri0|begin|endfunction|input|pmos|rtranif1|tri1|buf|endprimitive|integer|posedge|scalared|triand|bufif0|endspecify|join|primitive|small|trior|bufif1|endtable|large|pull0|specify|trireg|case|endtask|macromodule|pull1|specparam|vectored|casex|event|medium|pullup|strong0|wait|casez|for|module|pulldown|strong1|wand|cmos|force|nand|rcmos|supply0|weak0|deass|gn|forever|negedge|real|supply1|weak1|default|for|nmos|realtime|table|while|defparam|function|nor|reg|task|wire|disable|highz0|not|release|time|wor|edge|highz1|notif0|repeat|tran|xnor|else|if|notif1|rnmos|tranif0|xor)\b/g, 'sh_keyword', -1 ],
        [ /\b(?:generate|endgenerate|genvar|typedef|class|enum|union|struct|localparam|export|import|context|fork)\b/g, 'sh_keyword', -1 ],
        [ /~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\||@/g, 'sh_symbol', -1 ],
    ],
    [   // 1
        [ /$/g, null, -2 ],
        [ /(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g,
          'sh_url', -1 ],
    ],
    [   // 2
        [ /\*\//g, null, -2 ],
        [ /(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g,
          'sh_url', -1 ],
    ],
    [   // 3
        [ /\\"|\\\\/g, null, -1 ],
        [ /"/g, 'sh_string', -2 ],
    ],
    [   // 4
        [ /\*\)/g, 'sh_preproc', -2 ],
    ],
    [   // 5
        [ /$/g, 'sh_preproc', -2 ],
    ],
];

手書きで適当に書いたので不具合もありそうなのですが。

等幅フォントについて

ソースファイルの表示には等幅フォントを使わないと、 インデントがずれて気持ちの悪いことになってしまいます。

proportional.PNG

実は等幅フォント(monospace)を指定しても、firefox では日本語フォントと英語フォントとで幅が2:1にならずにずれてしまいました。

また、標準で使われる 'MS ゴシック' の英語フォントでゼロとオーの区別が つかなかったり、そもそも圧倒的にダサいのも問題です・・・

monospace.png

最近はWebフォントというのがあって、ローカルにインストールされていないフォントで ブラウザ上に文字を表示することが可能になっているため、これを使って上記の問題を何とか解決しようと 試みています。

今のところ、このサイトでは Google Fonts の Inconsolata を使っています。

http://www.google.com/fonts/specimen/Inconsolata
http://www.google.com/fonts#UsePlace:use/Collection:Inconsolata

LANG:javascript
function MMMMiiii() {                   // コメント
    var data = [                        // コメント
        '日本語データ',  12345,         // コメント
        'Alphabet data', 12345];        // コメント
    for(var i=0; i<data.length; i++) {  // コメント
        alert(data[i]);                 // コメント
    }

Windows 上で MSゴシック と合わせる限り、firefox においても かなりズレが小さくてすむことから採用している物です。 なぜか(?) Windows 版の IE や Chrome ではまったくずれないようです。

等幅 Google Fonts との組み合わせをいろいろ試してみた結果はこちらです。
http://dora.bk.tsukuba.ac.jp/~takeuchi/Monospace%20WebFonts.html
これまたなぜだか texthighlight に使った場合とズレ方が大きく異なるのですが、参考まで。

いまごろですが IE8 への対応

IE8 では sh_main.js にて "catch ステートメントでは適用されますが、throw ステートメントでは適用されません。" というエラーが出てうまく動いていないことに今頃になって気付きました。

これは、http://d.hatena.ne.jp/uupaa/20100127/1264566103 にあるように、throw で文字列を投げているのが理由だそうです。

そこで、sh_main.js にて

LANG:javascript
         throw 'HTTP error: status ' + request.status;

等となっていたところを、

LANG:javascript
         throw new Error('HTTP error: status ' + request.status);

と直したところ、曲がりなりにも動いているようです?

コメント





添付ファイル: filemonospace.png 538件 [詳細] fileproportional.PNG 561件 [詳細]

Counter: 7194 (from 2010/06/03), today: 1, yesterday: 0