色付きコードでブログを投稿しよう!
TextMateには言語のシンタックスに従って ソースコードを自動で色付けするSyntax Highlighting機能がある 複数のThemeが用意されており そこから自分の好みのカラーセットを選べる 現在の僕のお気に入りはCobaltだ
Syntax Highlightingはほんとうにソースコードを綺麗にみせる 自分の汚いコードがそれなりの作品に化けたように錯覚する 誰か日本語用Syntax Highlightingを作ってくれないかな そうしたら自分の日本語ももう少しマシに見えるのに
TextMateを使っていると このカラーを使った色付きコードでブログの投稿をしたい という欲求が当然に生じる
幸いなことに UltravioletというRubyの拡張ライブラリがそれを可能にしてくれる UltravioletはTextPowというTextMateのBundleを解析するライブラリを使い その解析結果に従ってソースコードをカラーリングする
早々Ultravioletを使って Haskell、Javascript、Lisp、Perl、Python、Rubyで書いたコードを投稿してみる (Ruby以外のコードは他のサイトから拝借しました)1
fact.hs with espresso_libre
1 factorial n 2 | n == 0 = 1 3 | otherwise = n * factorial (n-1)
fact.js with spacecadet
1 var fact = function(n) { 2 if(n==0) { 3 return 1; 4 } else { 5 return n * arguments.callee(n-1); 6 } 7 }
fact.lisp with brilliance_black
1 (defun factorial (n) 2 (if (<= n 1) 3 1 4 (* n (factorial (- n 1)))))
fact.pl with amy
1 sub fact { 2 my($n) = @_; 3 4 if ($n == 0) { 5 return 1; 6 } else { 7 return $n * fact($n - 1); 8 } 9 } 10
fact.py with all_hallows_eve
1 def fact(n): 2 if n == 0: 3 return 1L 4 else: 5 return n * fact(n - 1)
fact.rb with slush_poppies
1 def fact(n) 2 (1..n).inject { |mem, var| mem * var } 3 end
Ultravioletのインストールは以下のようにできるけど2
$ gem install -r ultraviolet --include-dependencies
TextPowで鬼車(oniguruma)正規表現ライブラリを使っているので3 予めそのインストールが必要になる4
Ultravioletでは外部cssファイルを読み出しているので その出力結果を直接ブログに張り付けるとうまくいかない だから以下のサンプルコード(hilite.rb)を書いた
hilite.rb with cobalt
1 #!/usr/bin/env ruby 2 require "rubygems" 3 require "uv" 4 5 class Hilite 6 BGCOLORS = {"all_hallows_eve" => {:bg => '#000000', :fg => '#ffffff'}, 7 "amy" => {:bg => '#200420', :fg => '#d1d0ff'}, 8 "blackboard" => {:bg => '#0d1021', :fg => '#f8f8f8'}, 9 "brilliance_black" => {:bg => '#0d0d0d', :fg => '#cccccc'}, 10 "brilliance_dull" => {:bg => '#0a0a0a', :fg => '#cdcdcd'}, 11 "cobalt" => {:bg => '#002444', :fg => '#e6e1dc'}, 12 "dawn" => {:bg => '#f9f9f9', :fg => '#080808'}, 13 "espresso_libre" => {:bg => '#2a211c', :fg => '#bcae9d'}, 14 "idle" => {:bg => '#ffffff', :fg => '#000000'}, 15 "iplastic" => {:bg => '#efefef', :fg => '#000000'}, 16 "lazy" => {:bg => '#ffffff', :fg => '#000000'}, 17 "mac_classic" => {:bg => '#ffffff', :fg => '#000000'}, 18 "magicwb_amiga" => {:bg => '#969696', :fg => '#000000'}, 19 "pastels_on_dark" => {:bg => '#211e1e', :fg => '#dadada'}, 20 "slush_poppies" => {:bg => '#f1f1f1', :fg => '#000000'}, 21 "spacecadet" => {:bg => '#0d0d0d', :fg => '#dde6cf'}, 22 "sunburst" => {:bg => '#000000', :fg => '#f8f8f8'}, 23 "twilight" => {:bg => '#141414', :fg => '#f8f8f8'}, 24 "zenburnesque" => {:bg => '#404040', :fg => '#dedede'} 25 } 26 FILE_TYPES = {'rb' => 'ruby', 'rjs' => 'ruby', 'rxml' => 'ruby_on_rails', 27 'rhtml' => 'ruby_on_rails', 'pl' => 'perl', 'pm' => 'perl', 28 'html' => 'html', 'applescript' => 'applescript', 29 'py' => 'python', 'js' => 'javascript', 'as' => 'actionscript', 30 'php' => 'php', 'c' => 'c', 'h' => 'c', 'java' => 'java', 31 'markdown' => 'markdown', 'sh' => 'shell-unix-generic', 32 'bashrc' => 'shell-unix-generic', 'sql' => 'sql', 33 'txt' => 'plain_text', 'textile' => 'textile', 34 'xml' => 'xml', 'tld' => 'xml', 'jsp' => 'xml', 35 'rss' => 'xml', 'yaml' => 'yaml', 'lisp' => 'lisp', 36 'hs' => 'haskell' 37 } 38 39 def initialize(file) 40 @extention = File.extname(file).delete('.') 41 @syntax = FILE_TYPES[@extention] 42 @data = IO.read(file) 43 end 44 45 Uv.themes.each do |theme| 46 define_method("parse_with_#{theme}") do 47 parse(theme, true) 48 end 49 end 50 51 def parse(theme, numbering) 52 html = Uv.parse( @data, "xhtml", @syntax, numbering, theme ) 53 54 css_file = File.join(Uv.path, %w(render xhtml files css), "#{theme}.css") 55 styles = {} 56 IO.read(css_file).gsub(/pre\.#{theme}\s+\.(\w+)\s*\{(.+?)\}/m) do 57 styles[$1] = $2.gsub(/\s/, "") 58 end 59 styles.each do |key, value| 60 html.gsub!(/class="#{key}"/i, %Q{style="#{value}"}) 61 end 62 html.gsub!(/class="#{theme}"/) { |match| match + 63 %Q{ style="background-color:#{BGCOLORS[theme][:bg]};color:#{BGCOLORS[theme][:fg]}"} } 64 end 65 end 66 67 if __FILE__ == $0 68 themes = Hilite::BGCOLORS.keys 69 ARGV.each do |file| 70 theme = themes[rand(themes.length)] 71 colored_code = Hilite.new(file).parse(theme, true) 72 puts "<p>#{file} with #{theme}</p>" + colored_code 73 end 74 # ARGV.each do |file| 75 # colored_code = Hilite.new(file).parse_with_cobalt 76 # puts "<p>#{file} with cobalt</p>" + colored_code 77 # end 78 end
使い方はターミナルで
$ ruby hilite.rb fact.rb > fact.html
とすれば fact.rbを色付けしたコードを含むfact.htmlが得られる 引数として複数のファイルを指定できる
サンプルでは複数のコードに対して ランダムにThemeを選択し出力している
69~73行をコメントアウトし 74~77行のコメントを外せば 全てのコードをCobalt Themeで色付けしたものが得られる
Hiliteクラスの基本的な使い方は
theme = 'cobalt'
code = Hilite.new(filename)
puts code.parse(theme, true)
ファイル名を引数としてHiliteクラスをオブジェクト化し ThemeとNumberingの有無を引数としてparseメソッドを呼ぶ あるいは
code = Hilite.new(filename)
puts code.parse_with_espresso_libre
puts code.parse_with_blackboard
のようにThemeをwithしたメソッドを呼んでもいい
Syntaxはファイルの拡張子から FILE_TYPESテーブルで一意に判断している 必要なSyntaxがテーブルにない場合は 適宜追加してほしい 使えるSyntaxはirbで
irb> require 'uv'
irb> Uv.syntaxes
などとすれば得られる
ちなみにTextMateに必要なSyntax Bundleがない場合には Manualの5.7 Getting More Bundles5を参照にして インストールしてほしい
何しろ素人が初めて投稿するコードなので いろいろとまずい点があると思うけど 使ってくれる人がいたらうれしい
(追記:2009/3/27) コードは以下にアップしてあります。Shoesによるインタフェースもあります。
- [スクリプト言語の比較::関数の再帰](http://pub.cozmixng.org/~the-rwiki/rw-cgi.rb?cmd=view;name=%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8%B8%C0%B8%EC%A4%CE%C8%E6%B3%D3%3A%3A%B4%D8%BF%F4%A4%CE%BA%C6%B5%A2 ↩
- [Ultraviolet](http://ultraviolet.rubyforge.org/ ↩
- [Textpow](http://textpow.rubyforge.org/ ↩
- [Install Oniguruma on OS X !](http://www.goodbyehelicopter.com/2008/02/20/install-oniguruma-on-os-x/ ↩
- http://manual.macromates.com/en/bundles ↩
blog comments powered by Disqus