Crayon Syntax Highlighterを退役させたりとか全て Pygments 化したりとか

proudlyてないぞ、Wordpress には、てなもんで、ネタとして WordPress ネタを出すのはアタシの性には合わんのであるけれど。

さらば Crayon Syntax Highlighter

Crayon Syntax Highlighterが、決して「お奨めじゃない」てことではなくて。ワタシのサイトには合わんのね。

で、多分半月くらいかけて、どうにかこうにか記事中の全コード片をPygmentsベースにリプレースした。のでCrayon Syntax Highlighterは引退させた。

Crayon Syntax Highlighterについての個人見解:

  1. 良いところ
    1. 読者自身で行番号表示制御できる
    2. 読者自身でハイライト表示制御できる
    3. 読者自身で折り返し制御できる
    4. 読者自身で別ウィンドウ表示制御できる
  2. 悪いところ
    1. 読みづらいスタイル
    2. 特に非対応言語でも身勝手にハイライトしてしまう
    3. 色が多すぎる上に色が薄すぎて、どこを目立たせたいのかさっぱりわからないことが多い
    4. 対応言語が(Pygments と較べると)少な過ぎる
    5. コンソールセッションのハイライトという発想そのものがない
    6. プラグインの振る舞いとして非常に乱暴

「良いところ」は Pygments 化で失われてしまったので、今後少しずつどうにかしていくつもり。「悪いところ」の「プラグインの振る舞いとして非常に乱暴」は、このサイトの読者にとってはどうでも良いことで、記事を書く側にのみ関係することなのだけど、これがなぁ、実にストレス溜まるのよ。普通 WordPress のプラグインて、執筆者は投稿フォームのテキストエリアに

1 [some_plugin param="hoge"]
2 some_plugin が処理するテキスト
3 [/some_plugin]

のように、「ショートコード」という記述をし、プラグインはこの「some_plugin が処理するテキスト」だけを処理するのが標準的なんだと思うのね。そうでない場合は、処理をしないように指示するシカケを用意すべきだと思うんだ。実は乱暴なのはCrayon Syntax Highlighterだけではなく、どうやら WordPress 本体もらしいのだが、何かつぅと、「プラグインを有効にしてあるだけで、普通に記述した <pre> 全てを Crayon Syntax Highlighter 色に染めてしまう」。Crayon Syntax Highlighterのビジュアルエディタを使うとこんなテキストを生成する:

1 <pre class="lang: python; decoded=true" >
2 &gt;&gt;&gt; import os
3 </pre>

のであって、だったら特定の class が付いた pre だけ処理すればいいのに、ということ。つまりな、Crayon Syntax Highlighterを活かしてあるだけで、「他の pre の使い方が出来ない」(共存出来ない)ことになるわけなのな。しかるにPygmentize onlineの生成結果を直接投稿エディタで貼り付けても勝手に Crayon Syntax Highlighter が処理してしまってうまくはいかない。(実際は table などのレンダリングも邪魔してくれる。)

ブログを始めたときからPygmentsにはいつかするぞ、と思いながら使っていたのだから、機能的にもそんなに満足して使っていたのではないけれど、さすがにこの乱暴さには段々と怒りが強くなってきて、今となっては「不十分だけどまぁまぁ使える」という評価すらしたくなくなってしまったわ。

とはいえ本格的 Pygments 化は今後も続く

「カスタム検索」でない方の検索ボックス:

これで「[code」「[pygmentize」という検索をすると、Pygments でハイライトしてる記事が全部検索できます。

でな、例えば「Microsoft Visual C++ Compiler for Python 2.7はひとのためならず」とか「日出日没計算、やってみよう」とか、「「前の投稿」「次の投稿」を改善」「Microsoft Visual C/C++ のインポートライブラリからシンボル検索」を見て欲しいんだけどね、横に溢れたときにさ、折り返すのはいいんだけどね、折り返した行とそうでない行、区別出来ますか? (一応出来ると思うんだ。)

ここ、実はとても苦心したとこでさ。まぁあんまり細かなこといっても仕方ないと思うけどね、「素の WordPress と出回ってる WordPress の Pygments プラグイン」だけではこれ、出来んです。実際何したかってぇとさ、Pygment Itつぅのを、完全に書き換えました。まだ「俺様にだけ良ければ良い」(今後も?)というシロモノなんで、当面はお披露目する気はないですがね。

Pygment Itの完成度はそもそもかなり低いです。のでね、いずれにしても改造はしたんだろうけど、一番問題だったのがこの折り返し問題についてだったのね。(「完成度はそもそもかなり低い」については、バックスラッシュがキャッシュ後に失われてしまう、という致命傷が残っており、かつ、WAF (Web Application Firewall) との相性も至極悪い。)個人的にまだまだお遊びレベルで作ったpygments onlineを実際に動かしてみればわかりますが、Pygments によるハイライトは、html ではこんな構造です:

 1 <div class="highlight">
 2 <pre>
 3 <span class="gp">&gt;&gt;&gt; </span><span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
 4 <span class="gt">Traceback (most recent call last):</span>
 5   File <span class="nb">&quot;&lt;stdin&gt;&quot;</span>, line <span class="m">1</span>, in <span class="n">&lt;module&gt;</span>
 6 <span class="gr">TypeError</span>: <span class="n">sequence item 0: expected string, int found</span>
 7 <span class="gp">&gt;&gt;&gt; </span><span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)))</span>
 8 <span class="go">&#39;0\n1\n2\n3\n4&#39;</span>
 9 </pre>
10 </div>

*これを css だけで折り返しのスマートな制御をすることは、アタシには出来なかった。css で関係するのは、white-space、word-wrap、overflow-x あたりなんだけどね、Wordpress のテーマの複雑さも絡んで、断念。どうしたか。もうね、Pygments が生成する html を加工しちゃった。pre 内の各行頭にね、

1 <span class='sitepygments-bol'></span>

なんて span を挿入した上で、css ではこんな制御:

1 pre {
2   line-height: 1.1; /* 標準より非常に狭く(通常は1.4~1.5が読みやすい) */
3 }
4 .sitepygments-bol {
5   /* pre での行間をせまくするかわりに、この sitepygments-bol を
6      場所取りとして使う。*/
7   display: block;
8   height: 0.5em;
9 }

こうすることで、折り返した場合には折り返した結果の複数行は line-height が効いて「詰まって」レンダリングされて、折り返しでない通常の改行が起こると 0.5em という割と標準的な行間を生み出す。

さて、Crayon Syntax Highlighterの良いところ

  1. 読者自身で行番号表示制御できる
  2. 読者自身でハイライト表示制御できる
  3. 読者自身で折り返し制御できる
  4. 読者自身で別ウィンドウ表示制御できる

を手放してしまったもののうち、これで3つ目はまぁ読者自身が制御しなくても良いかなぁ、と思うので良いとして、また、個人的には「行番号なんか別にいらねーんじゃね?」と思うので、当面考える気はないし、Pygments のハイライトは「ハイライトすんなよ」と思うようなものではないので制御しなくても良いだろうし、ってことで、悔やまれるのはやっぱり「別ウィンドウ表示」なのね。あとね。Python 公式ドキュメントサイトに行ってもらうとわかるんだけど、Python コンソールセッションやシェルセッション:

 1 >>> x = int(raw_input("Please enter an integer: "))
 2 Please enter an integer: 42
 3 >>> if x < 0:
 4 ...      x = 0
 5 ...      print 'Negative changed to zero'
 6 ... elif x == 0:
 7 ...      print 'Zero'
 8 ... elif x == 1:
 9 ...      print 'Single'
10 ... else:
11 ...      print 'More'
12 ...
13 More

を、実際に対話モード Python に渡す部分のみ:

 1 x = int(raw_input("Please enter an integer: "))
 2 
 3 if x < 0:
 4      x = 0
 5      print 'Negative changed to zero'
 6 elif x == 0:
 7      print 'Zero'
 8 elif x == 1:
 9      print 'Single'
10 else:
11      print 'More'

だけにする「機能(Show the prompts and ouput)」が作れたりします、Pygments ベースにしてると。そ。span についてる特定の class をみて表示非表示制御すんのね。「別ウィンドウ表示」も、「Show the prompts and ouput」も、せっかくプラグインを改造し始めてるんだから、多分そのうちやると思います。特に「Show the prompts and ouput」は個人的に大好きな機能だったりもしてさ。便利なんだよね、評価検証に。

なんかあんまり誰も得しそうにない情報ですまんね。書き換えたプラグインは、整理できて、なおかつよっぽど気が向けば公開するかもです。期待せずに期待しとけや。

2015-06-19 copybutton 実装

「機能(Show the prompts and ouput)」はすんなり出来た。

この記事中の「Python コンソールセッションやシェルセッション」と書かれてるすぐ下のですぐ試せます。右上の「>>>」ボタンね。

2015-06-20 行番号表示とそのトグルの実現

ここでは苦心して複雑なことをしていたが、どうせ行番号が欲しくなるケースはあるのだから、ということで、抜本的に考え方から変えた。

といっても、変更後が「簡単」かといえば別にそんなこともなくて。簡単になったのはレイアウトのコントロールしやすさだけね。実現のためのコードが簡単てわけではない。

まず最初に気付いたのは、sitepygments-bol なんて span を独自に追加しないでも良いのだ、ということ。pygments の行番号出力の方式には2種類あるのね。一つ目は html の table で実現するもので、こっちがデフォルトなので知っていたわけだ。単純化すればこんな構造:

 1 <table>
 2 <tr>
 3   <!--行番号だけの列-->
 4   <td>
 5   <pre>
 6 1
 7 2
 8 3
 9 4
10   </pre>
11   </td>
12 
13   <!--コードだけの列-->
14   <td>
15   <pre>
16 #! /bin/kattsu/eye
17 snip -uchi naomi
18 snip -uchi asami
19 snip -uchi anna
20   </pre>
21   </td>
22 </tr>
23 </table>

もう一方は pygments のソースみてて気付いた。ドキュメントで気付けってのな。こんな:

1 <pre>
2 <span class="lineno"> 1 </span>#! /bin/kattsu/eye
3 <span class="lineno"> 2 </span>snip -uchi naomi
4 <span class="lineno"> 3 </span>snip -uchi asami
5 <span class="lineno"> 4 </span>snip -uchi anna
6 </pre>

生の pygments へは、前者が「linenos=”table”」、後者が「linenos=”inline”」で指示します。

前者がね、いわゆる「折り返し問題」との格闘が凄まじいことになるわけ。だって行番号列とコード列を同期する方法、ないでしょ。コード行での一行ごとの tr ではないからね(*)。でも後者ならこれはどうにかなる!

まずさ、「行番号表示の効能」てのはな、ひとつには「説明で引用」ということをしたい、ということがあるよね。これがまぁ普通のニーズね。なんだけどさ、「折り返し行なのかそうでないのか」の識別にもなるでしょう? のでね、「行番号表示制御をする」ことこそが、最初のアプローチにおける「複雑怪奇なレイアウトコントロール」とおさらばするための、絶対的正解、とは言えるわけだわね。

完成版のデフォルトの css と html では、class=”lineno” に対して、何もしません。line-height もそのまま。そのかわりに、copybutton にならった行番号表示制御ボタンでもってスタイルを制御します。javascirpt を全文引用するとこんな:

 1 jQuery(function($) {
 2   function toggle_lnelems(lnelems, show) {
 3     var styles = {
 4       'position': 'absolute', 'left': '-1.1em',
 5       'display': 'block'
 6     };
 7     lnelems.each(function(index) {
 8       var e = $(this);
 9       if (show) {
10         e.css(styles);
11         e.css('visibility', 'visible');
12       } else {
13         e.css(styles);
14         e.css('visibility', 'hidden');
15       }
16     });
17   }
18 
19   $(document).ready(function() {
20     /* Add a [+#] button on the top-left corner of code samples to toggle
21      * linenos and thus make the code
22      * copyable. */
23 
24     var div = $('.pygment .highlight');
25     var pre = div.find('pre');
26 
27     toggle_lnelems(div.find(".lineno"), false);
28 
29     // get the styles from the current theme
30     pre.parent().parent().css('position', 'relative');
31     var hide_text = 'Hide the linenos';
32     var show_text = 'Show the linenos';
33     var border_width = pre.css('border-top-width');
34     var border_style = pre.css('border-top-style');
35     var border_color = pre.css('border-top-color');
36     var button_styles = {
37       'cursor':'pointer', 'position': 'absolute', 'top': '0', 'left': '-1em',
38       'border-color': border_color, 'border-style': border_style,
39       'border-width': border_width, 'color': border_color, 'text-size': '75%',
40       'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em'
41     }
42   
43     // create and add the button to all the code blocks that contain lineno
44     div.each(function(index) {
45       var jthis = $(this);
46       if (jthis.find('.lineno').length > 0) {
47         var button = $('<span class="linenosbutton">+#</span>');
48         button.css(button_styles)
49         button.attr('title', show_text);
50         jthis.prepend(button);
51       }
52     });
53   
54     // define the behavior of the button when it's clicked
55     $('.linenosbutton').toggle(
56       function() {
57         var button = $(this);
58         toggle_lnelems(button.parent().find('.lineno'), true);
59         button.attr('title', hide_text);
60       },
61       function() {
62         var button = $(this);
63         toggle_lnelems(button.parent().find('.lineno'), false);
64         button.attr('title', show_text);
65       });
66   });
67 });

元にしている copybutton.js の実装が綺麗だったもんで、おかげで jQuery のありがたさが急激に理解できてきたわ。のでね、この js、2時間かからずに書けちゃった。というわけでは予告の通りの「実現のためのコードが簡単てわけではない」というほどでもないんだけど、それもこれも jQuery のおかげというだけね。

2015-07-12 WP Pygments Syntax Highlighter として結実

WP Pygments Syntax Highlighterに仕上げました。

もともとは「Pygment It」という別のプラグインだったんだけれど、まったく面影ないほどに全編書き直してます。ソースコードだけみたら「Pygment It」を元にしたなんて、わからないかもしれない。

この記事で書いてたようなことの「完成品」は、全部https://bitbucket.org/hhsprings/wp-pygments-syntax-highlighter/で見ることが出来ます。