Vimでカラーコードをインクリメントする
Vim Advent Calendar2013 164日目の記事となります。
再開ということで、書きます。何気に3回目。
ゴールデンウィークも終わりましたが、いかがお過ごしでしょうか。
さて、社会人になって早くも1年が経過し、2年目に突入しました。
まとまった休みのありがたみを嫌というほど実感しますね。
さて今回は、初めてVim scriptを書いたというお話です。
同じタイトルの記事を数日前に書いていたのですが、中途半端だったので、
書き直し + VACの記事にしちゃいました。
Vim scriptと正面から向き合う
Vimの使い始めた経緯などは、私とVimを読んでいただければと思います。
あれから月日が経ちましたが、Vim scriptを「やらなきゃ」とは思っていたものの、一歩が踏み出せずにいました。
そこで出会った記事がこちらです。
Vim - プラグインまたはVim scriptを書く楽しさについて語りたい - ぼっち勉強会
一度は目を通していたものの、改めて真剣に読んでみて、かなり背中を押されました。
ネタが浮かばない。がまさに自分で、「やりたい」けど「やれない」の理由は大半がこれです。
そして、世の中には思いついても、ない物の方が少なくて、ほぼ絶対あるんですよね。
ですが、あるからやらないだと、一生身に付きません。
Vim scriptに関しては、下記記事が非常に参考になりました。
Vimスクリプト基礎文法最速マスター - 永遠に未完成
Big Sky :: モテる男のVim Script短期集中講座
Vim Advent Calendar 2013 136日目:Vim script で関数のデフォルト引数を設定したい - C++でゲームプログラミング
大体一度は目を通していたのですが、モチベーションが高い時に読むのと、
普段の状態で読むのとでは、頭への内容の入り方も全然違います。
それと、よく言われますがあとは「ヘルプを読みましょう」ということです。
今回、一番感じたことは、ヘルプを読むことの重要さです。
全てがここにあります。本当に。。。
本題
Vimには<C-a>
でインクリメント、<C-x>
でデクリメントという、
ちょっとした数値変更にとても便利な機能があります。
8、16進数、アルファベットにも対応ができ、なかなか面白いです。
8進数、アルファベットは置いておいて、16進数に目をつけましょう。
0x
を前につけると、16進数と解釈します。
ですが、普段16進数を使う時はどのような時が多いでしょうか。
#RRGGBB
の形ではないでしょうか。
最近、仕事の関係で、グラフやマップなど、データの視覚化を扱っており、
style
系のプロパティでcolor設定は大体これです。
そこで思いました。
カラーコードのインクリメント、デクリメント。出来たらどうだろう。
(探せば絶対あると思う)
処理を考える
- カーソル下のカラーコードを取得
#RRGGBB
形式かをチェック- インクリメント or デクリメント
- 更新する
ささっと思ったのがこの4つで、初めて書くのには丁度良さそうでした。
無理矢理実装
初めて書いたVim scriptになります。
なかなかひどいコードですが最初ですので…!!
function! s:Color_Fluctuation(type, ...) " 第2引数がなければ '' let color = get(a:, 1, '') " カラーコード let colorcode = expand('<cfile>') " カーソル位置 let pos = getpos(".") " check if !s:Check_ColorCode(colorcode) echo colorcode. ' is not color code.' return 0 endif " r,g,bで分離 let red = str2nr(colorcode[1:2], 16) let green = str2nr(colorcode[3:4], 16) let blue = str2nr(colorcode[5:6], 16) let c_dict = {'red': red, 'green': green, 'blue': blue} if a:type " increment let new_dict = s:Color_Increment(color, c_dict) else " decrement let new_dict = s:Color_Decrement(color, c_dict) endif let new_color = s:Get_ColorCode(new_dict) " replace execute 's/'.colorcode.'/'.new_color.'/g' call setpos('.', pos) endfunction
" ColorCord Increment function! s:Color_Increment(color, c_dict) let max = 255 let min = 0 for key in keys(a:c_dict) if a:color != '' if a:color == key && s:Check_Range(a:c_dict[key], max, min) let a:c_dict[key] = a:c_dict[key] + 1 endif else if s:Check_Range(a:c_dict[key], max, min) let a:c_dict[key] = a:c_dict[key] + 1 endif endif endfor return a:c_dict endfunction " ColorCord Decrement function! s:Color_Decrement(color, c_dict) let max = 256 let min = 1 for key in keys(a:c_dict) if a:color != '' if a:color == key && s:Check_Range(a:c_dict[key], max, min) let a:c_dict[key] = a:c_dict[key] - 1 endif else if s:Check_Range(a:c_dict[key], max, min) let a:c_dict[key] = a:c_dict[key] - 1 endif endif endfor return a:c_dict endfunction
" #RRGGBBの形かチェック function! s:Check_ColorCode(code) if a:code =~ '^\#\{1}\x\{6}$' return 1 else return 0 endif endfunction " 範囲内かチェック function! s:Check_Range(color, max, min) if a:min <= a:color && a:color < a:max return 1 else return 0 endif endfunction function! s:Get_ColorCode(new_dict) let s:V = vital#of('vital') let s:M = s:V.import('Data.String') let code = '#' let show = 'Red:'. a:new_dict.red. ' Green:'. a:new_dict.green. ' Blue:'. a:new_dict.blue for key in keys(a:new_dict) " 10 -> 16 let val = s:M.nr2hex(a:new_dict[key]) if strlen(val) == 1 let val = '0'.val elseif strlen(val) == 0 let val = '00' endif let a:new_dict[key] = val endfor " echo echo show " create new color code. let code = code. a:new_dict.red. a:new_dict.green. a:new_dict.blue return code endfunction
コマンド定義とマッピング
そして、無理矢理には無理矢理なマッピング。。。
※<C-A>
は+
、<C-X>
は-
にしてあります。
command! -nargs=* ColorIncrement call s:Color_Fluctuation(<f-args>) nnoremap <C-A> :ColorIncrement 1<CR> nnoremap <Left> :ColorIncrement 1 red<CR> nnoremap <Up> :ColorIncrement 1 green<CR> nnoremap <Right> :ColorIncrement 1 blue<CR> command! -nargs=* ColorDecrement call s:Color_Fluctuation(<f-args>) nnoremap <C-X> :ColorDecrement 0<CR> nnoremap <S-Left> :ColorDecrement 0 red<CR> nnoremap <S-Down> :ColorDecrement 0 green<CR> nnoremap <S-Right> :ColorDecrement 0 blue<CR>
左からRGBなので、
インクリメントは、R=<Left>
G=<Up>
B=<Right>
RGB全て=<C-A>
デクリメントは、R=<S-Left>
G=<S-Down>
B=<S-Right>
RGB全て=<C-X>
としました。
動き
NeoBundle 'lilydjwg/colorizer'
colorizerを入れて、色の変化が分かりやすいようにしています。
まとめ
一行まるまる置換しているので、同じ行に同じコードがあれば、全部変わってしまうことと、
よくもまぁこんなに無理矢理書いたなぁと思うことなど、心残りはありますが、
Vim script
にとりあえず触れてみて、何か作ってみるという点で見れば、得た物は大きかったです。
- 代入の方法
- 辞書の使い方
- 関数定義
- 呼び出し方
- 引数へのデフォルト値設定
- コマンド定義
- Vital.vimの使い方
結構いろいろ学べています。
コードが綺麗やお行儀なんてものは後からでもどうにでもなりますし、
まずは触れてみて、何でも良いからやってみる。このハードルを超えてみることをオススメします。
写経でも、簡単なものでも、やはりできた、作ったが一番のモチベーションです。
とりあえず、何が言いたいかというと!
やめられない 止まらない Vim script
6月にはmomonga.vim#4もありますので、それまでにモチベーションが上げられたことも、
今回良かったことですね。
にしても、最近コード書いてると_
の使用率が。。。
以上です。
次回は、せっかくVim scriptに触れたので、まだ触れられずにいる人向けの物が書けたらと思います。
(こっそり修正2 2014/05/14 9:25)
最後に
VAC2013、ぜひ書きましょう!