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によるインタフェースもあります。

gist: 1451 - GitHub

  1. [スクリプト言語の比較::関数の再帰](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
  2. [Ultraviolet](http://ultraviolet.rubyforge.org/
  3. [Textpow](http://textpow.rubyforge.org/
  4. [Install Oniguruma on OS X !](http://www.goodbyehelicopter.com/2008/02/20/install-oniguruma-on-os-x/
  5. http://manual.macromates.com/en/bundles


blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS