proudlyてないぞ、Wordpress には、てなもんで、ネタとして WordPress ネタを出すのはアタシの性には合わんのであるけれど。
Contents
さらば Crayon Syntax Highlighter
Crayon Syntax Highlighterが、決して「お奨めじゃない」てことではなくて。ワタシのサイトには合わんのね。
で、多分半月くらいかけて、どうにかこうにか記事中の全コード片をPygmentsベースにリプレースした。のでCrayon Syntax Highlighterは引退させた。
Crayon Syntax Highlighterについての個人見解:
- 良いところ
- 読者自身で行番号表示制御できる
- 読者自身でハイライト表示制御できる
- 読者自身で折り返し制御できる
- 読者自身で別ウィンドウ表示制御できる
- 悪いところ
- 読みづらいスタイル
- 特に非対応言語でも身勝手にハイライトしてしまう
- 色が多すぎる上に色が薄すぎて、どこを目立たせたいのかさっぱりわからないことが多い
- 対応言語が(Pygments と較べると)少な過ぎる
- コンソールセッションのハイライトという発想そのものがない
- プラグインの振る舞いとして非常に乱暴
「良いところ」は 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 >>> 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">>>> </span><span class="s">"</span><span class="se">\n</span><span class="s">"</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">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
6 <span class="gr">TypeError</span>: <span class="n">sequence item 0: expected string, int found</span>
7 <span class="gp">>>> </span><span class="s">"</span><span class="se">\n</span><span class="s">"</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">'0\n1\n2\n3\n4'</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の良いところ
- 読者自身で行番号表示制御できる
- 読者自身でハイライト表示制御できる
- 読者自身で折り返し制御できる
- 読者自身で別ウィンドウ表示制御できる
を手放してしまったもののうち、これで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」は個人的に大好きな機能だったりもしてさ。便利なんだよね、評価検証に。
なんかあんまり誰も得しそうにない情報ですまんね。書き換えたプラグインは、整理できて、なおかつよっぽど気が向けば公開するかもです。期待せずに期待しとけや。
「機能(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/で見ることが出来ます。