CSSの整形を正規表現だけで何とかする

最近見た2000行ぐらいのCSS、いろんな人が好き勝手編集していてインデントとかその他もろもろが地獄っぽかったので、正規表現だけで何とかして修正しようとした。

エントロピー

100行程度の規模なら1行ずつ見ていって修正すれば良いけど、人為的な作業である以上必ずエントロピーは拡大していって、定期的に「すみませんでした、死にます」「いえいえすみませんでした、死にます」「いえいえいえすみ」という感じになってみんなの心が濁っていく。生まれる前に消し去りたい。

失敗した方法

最初に考えた方法は、Vimのシンタックス('='キー)に任せて整えてもらう方法だったけど、複数ファイルに適用するときに面倒だし、Vimだしモテないし、行頭行末のスペース程度しか整わないし失敗した。
次に考えた方法は、CSSと互換性のあるSassまたはLessのようなCSS拡張言語を利用して、通常のCSSを再度CSSに変換することで整形されたものを得ようという方法だった。中で何が行われているか分からなくてわりと怖かったのと、filter: progid:DXImageとか特殊なものを認識しないとか、整形形式を細かく調整できないとか色々な問題があって失敗した。よく調べれば回避できるかもしれないし、Sassienceみたいなものもあるので、この方法は今は採用しなくてももう少し調べたい。

正規表現でやる方法

最終的に落ち着いた方法は、ヒューリスティック正規表現書いて静的解析で何とかする方法で、この方法がいまは1番素朴で分かりやすくて良いなという感じがした。git-push時のhooksか、ciで妥当かどうかだけでもチェックするというのが良さそうで、おかしいところが見つかればローカルで適用して書き換えて、gitのdiffとかでざっと確認するというのが手抜き感あって最初は楽で良さそう。何やってるのか分からないけど書き換わってる、みたいなのが1番怖い。あとコーディング規約がコードで書かれているというのは面白いかもしれない。いま適当にスクリプト書いてみたけど、改善の余地がかなりありそう。

#!/usr/bin/env ruby
# usage: cat hell.css | ruby $0 > heaven.css

str = STDIN.read

# 末尾の半角スペースを削除
str.gsub!(/ +$/, "")

# コロンの前に半角スペース
str.gsub!(/ +,/, ",")

# 中括弧の前に半角スペース
str.gsub!(/([^ ]){/, '\1 {')

# 中に何も記述のない中括弧を削除
str.gsub!(/.*{[\s\n]*}\n/, "")

# 1〜7個のインデントを4個に
str.gsub!(/^ {1,7}([^ ])/, '    \1')

# タブ文字を半角スペースに
str.gsub!(/\t/, "    ")

# コロンの後に半角スペースを入れる
# 但しコロンの後が'D'か'\'のときは入れない(http://と、filter: progid:DXImage対策)
str.gsub!(/^( .*):([^ ^D^\/])/, '\1: \2')

# background: url()no-repeat とかの括弧の後に半角スペース
str.gsub!(/\)no-repeat/, ") no-repeat")
str.gsub!(/\)repeat/, ") repeat")

# 先頭が'}'か'#'か'.'のときはインデントしない
# 但しカラーコードの#ぽい場合は無視
# @media only screen ... { } を使っている場合は困る
str.gsub!(/^ +([.}])/, '\1')
str.gsub!(/^ +#([^\d^A-F^a-f])/, '#\1')

print str

FileMerge.app

MacだとXcodeにFileMerge.appという差分表示用のアプリが標準で入っていて、大量のCSSの書き換えとかを行った後の比較に役に立つ。


結局色々考えてみて、コーディング規約を守ろうとする思いと守らせる環境が必要、みたいな普通の感想を得た。「だいじなのは こころ」って昔やったゲームのエンディングで言ってた。幻獣界に住む女の子が、主人公に対して言った台詞だった気がする。急に思い出してびっくりした。小さかった頃に、登場するモンスターとボス戦の音楽が怖くて、父親にやってもらって後ろで見てたの思い出した。