GVIMで編集中のスタイルシートの色をプレビューするcss_color.vimを256色ターミナル環境(Vim)でも動かすパッチ

6/30: 本パッチがcss_color.vim 0.7で正式に採用されました

6/12 19:15: 公開したパッチに不具合があったので修正
css_color.vimという,編集中のスタイルシート(*.css)の色(#FF0000)などに,シンタックスハイライトの設定をリアルタイムかつ動的に適用する事で,色をプレビューしながら編集出来るcss.vimというプラグインがあります.

css_color.vim - CSS color preview : vim online

http://lanpartei.de/~niklas/vim/after/syntax/css-color-vim.png

これは大変すばらしいのですが,GVIM用に書かれているため,内部でシンタックスハイライトの設定を"#RRGGBB"のような16進数で行っています.256色表示に対応したターミナルの環境でVimを使うには,このような形式ではなくxtermの256の固定パレット番号でシンタックスハイライトの指定をする必要があります,こういった環境を用意するには,xtermのコンパイルオプション(--256-colors)を設定したり,MacだとiTermというターミナルエミュレータを使うなどいろいろあります.

vim 256 - Google 検索

その一方で,GVIM用に作られたvimのカラースキームを256ターミナルで使うための試みが行われています.

frexx.de

ここでは#FF0000のような文字列をxterm-256colorのパレット番号に変換するツールが公開されています.このツールは

0. 「xtermのパレット番号と対応する(R,G,B)の組み合わせのマッピング」をあらかじめ用意する

入力:"#FF0000" のような文字列

1.  入力文字列をR, G, Bそれぞれの値に分解する
2.  あらかじめ用意したマッピングのうち,色空間上でもっとも近い色を選び,それを返す

出力:入力に対応するxtermのパレット番号

といったことをやっています.しかしこのツールはC言語で実装されているため,そのままではVimプラグインとして組み込む事は出来ません.

しかしそれほど難しいことをやっているわけではないので,これをVim scriptに移植し,上記のcss_color.vimの該当する箇所に組み込みました.具体的には現在の環境がターミナル(has("gui_running")が偽)で,使用可能な色数を示すt_Co変数の値が256だったときに,上記処理を適用するようになっています,

すでに作者の方にはパッチをメールでお送りしたので,次のリリースで反映されるかもしれませんが,自分で試したい方は以下にパッチを張りますのでよろしければどうぞ.

以下2008-02-12にリリースされたバージョン0.6のcss_color.vimに対するパッチです.元のソースコードはこのページから入手する事が出来ます > css_color.vim - CSS color preview : vim online

--- css.vim	2008-06-11 00:54:08.000000000 -0700
+++ css.vim.new	2008-06-11 03:12:38.000000000 -0700
@@ -26,20 +26,94 @@
    if s:currentmatch !~ a:pat.'\/'
       exe 'syn match '.group.' /'.a:pat.'\>/ contained'
       exe 'syn cluster cssColors add='.group
-      exe 'hi '.group.' guifg='.s:FGforBG(a:clr)
-      exe 'hi '.group.' guibg='.a:clr
+      if has('gui_running')
+        exe 'hi '.group.' guifg='.s:FGforBG(a:clr)
+        exe 'hi '.group.' guibg='.a:clr
+      elseif &t_Co == 256
+        exe 'hi '.group.' ctermfg='.s:Rgb2xterm(s:FGforBG(a:clr))
+        exe 'hi '.group.' ctermbg='.s:Rgb2xterm(a:clr)
+      endif
       return 1
    else
       return 0
    endif
 endfunction
 
+"" the 6 value iterations in the xterm color cube
+let s:valuerange = [ 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF ]
+"
+"" 16 basic colors
+let s:basic16 = [ [ 0x00, 0x00, 0x00 ], [ 0xCD, 0x00, 0x00 ], [ 0x00, 0xCD, 0x00 ], [ 0xCD, 0xCD, 0x00 ], [ 0x00, 0x00, 0xEE ], [ 0xCD, 0x00, 0xCD ], [ 0x00, 0xCD, 0xCD ], [ 0xE5, 0xE5, 0xE5 ], [ 0x7F, 0x7F, 0x7F ], [ 0xFF, 0x00, 0x00 ], [ 0x00, 0xFF, 0x00 ], [ 0xFF, 0xFF, 0x00 ], [ 0x5C, 0x5C, 0xFF ], [ 0xFF, 0x00, 0xFF ], [ 0x00, 0xFF, 0xFF ], [ 0xFF, 0xFF, 0xFF ] ]
+:
+function! s:Xterm2rgb(color) 
+	" 16 basic colors
+   let r=0
+   let g=0
+   let b=0
+   if a:color<16
+      let r = s:basic16[a:color][0]
+      let g = s:basic16[a:color][1]
+      let b = s:basic16[a:color][2]
+   endif
+	
+	" color cube color
+   if a:color>=16 && a:color<=232
+      let color=a:color-16
+      let r = s:valuerange[(color/36)%6]
+      let g = s:valuerange[(color/6)%6]
+      let b = s:valuerange[color%6]
+   endif
+	
+	" gray tone
+	if a:color>=233 && a:color<=253
+      let r=8+(a:color-232)*0x0a
+      let g=r
+      let b=r
+   endif
+   let rgb=[r,g,b]
+   return rgb
+endfunction
+
+function! s:pow(x, n)
+   let x = a:x
+   for i in range(a:n-1)
+      let x = x*a:x
+   return x
+endfunction
+
+let s:colortable=[]
+for c in range(0, 254)
+   let color = s:Xterm2rgb(c)
+   call add(s:colortable, color)
+endfor
+
+" selects the nearest xterm color for a rgb value like #FF0000
+function! s:Rgb2xterm(color)
+   let best_match=0
+   let smallest_distance = 10000000000
+   let r = eval('0x'.a:color[1].a:color[2])
+   let g = eval('0x'.a:color[3].a:color[4])
+   let b = eval('0x'.a:color[5].a:color[6])
+   for c in range(0,254)
+      let d = s:pow(s:colortable[c][0]-r,2) + s:pow(s:colortable[c][1]-g,2) + s:pow(s:colortable[c][2]-b,2)
+      if d<smallest_distance
+      let smallest_distance = d
+      let best_match = c
+      endif
+   endfor
+   return best_match
+endfunction
+
 function! s:SetNamedColor(clr,name)
    let group = 'cssColor'.substitute(a:clr,'^#','','')
    exe 'syn keyword '.group.' '.a:name.' contained'
    exe 'syn cluster cssColors add='.group
-   exe 'hi '.group.' guifg='.s:FGforBG(a:clr)
-   exe 'hi '.group.' guibg='.a:clr
+   if has('gui_running')
+     exe 'hi '.group.' guifg='.s:FGforBG(a:clr)
+     exe 'hi '.group.' guibg='.a:clr
+   elseif &t_Co == 256
+     exe 'hi '.group.' ctermfg='.s:Rgb2xterm(s:FGforBG(a:clr))
+     exe 'hi '.group.' ctermbg='.s:Rgb2xterm(a:clr)
    return 23
 endfunction
 
@@ -65,7 +139,7 @@
    endif
 endfunction
 
-if has("gui_running")
+if has("gui_running") || &t_Co==256
    " HACK modify cssDefinition to add @cssColors to its contains
    redir => s:olddef
       silent!  syn list cssDefinition