WordPress で外部のソースコードを埋め込んでシンタックスハイライトを適用する

WordPress の記事にプログラムを載せるときには SyntaxHighlighter Evolved などのプラグインのお世話になるわけだが,ソースが外部にある場合,いちいちコピーして貼り付けるのは効率が悪いし,訂正もしづらい.だったら gist を使うという手もあるが,bash のログに至るまで全部 gist に上げるというのもちょっとスゴすぎるし,かといって言語によってコードボックスのスタイルが異なるというのも格好悪い.結局,記事内にリンクだけ書くと,リンク先のコンテンツを引っ張ってきてハイライトするフックを書くことにした.

インターフェースと仕掛け

記事内に [excode lang=”…” src=”…”] という風にショートコードを書くと,src で指定した URL からコンテンツを引っ張ってきて,SyntaxHighlighter Evolved でハイライトする.excode に渡される他のオプションは,そのまま全部 SyntaxHighlighter Evolved に渡されるようになっている.

中でやっていることは単純で,本文から excode を探し出してコンテンツを取得し,[code lang="..."] … [/code] の形に置き換えるだけ.SyntaxHighlighter Evolved は priority 7 の the_content フィルターとして登録されているので,これより先に処理を行うために,priority 6 で the_content に登録している.SyntaxHighlighter Evolved 以外のハイライトプラグインでも,priority だけ調整すれば使えると思う.

コンテンツの取得は file_get_contents しているだけなので,gist に限らず,web 上にあるリソースなら何でも取ってこれる.記事のコメントで使えるようにすると大変なことになりそうだ.

導入

次のコードをテーマの functions.php に追加するだけでよい.もちろんこのコードも excode で埋め込まれている.

function excode_filter($content) {
	return preg_replace_callback('/(\\[?)(\\[excode ([^\\]]*?)\\])\\]?/', function($m) {
		if ($m[1]) return $m[2];
		$out = '[' . 'code';
		$pairs = preg_split("/\s+/", $m[3]);
		foreach ($pairs as $pair) {
			list($key, $value) = explode('=', $pair);
			if ($key == 'src') $src_url = preg_replace('/["\']/', '', $value);
			elseif ($key == 'link') $link_url = preg_replace('/["\']/', '', $value);
			elseif ($key == 'title') $title = preg_replace('/["\']/', '', $value);
			else $out .= ' ' . $pair;
		}
		$link_url = $link_url ? $link_url : $src_url;
		$title = $title ? $title : $link_url;
		$src = $src_url ? file_get_contents($src_url) : FALSE;
		$out .= sprintf(' title=\'<a href="%s">%s</a>\']', $link_url, $title);
		$out .= $src !== FALSE ? $src : '(Not available)';
		$out .= '[' . '/code]';
		return $out;
	}, $content);
}
add_filter('the_content', 'excode_filter', 6);

おまけ

上のコードを貼り付けようとして気づいたが,code ブロックの中に “[code” という文字が出現すると,その先の表示が崩れてしまう.上を見てもらえればわかるが,それを防ぐためだけにわざと文字列を分割している箇所がある.見つけたときちょっと笑ってしまった.

おまけ 2

本当は xcode にしたかった.

コメントを残す

メールアドレスが公開されることはありません。