社内では仕事柄、デモアプリを作るためにテキストエディタでhtmlやcss、jsファイルを書く事が多々あります。
お客様からさまざまなプロジェクトのファイルをお預かりして、デザイナーさんが作った画面イメージを実際のウェブページとして仕上げて、Javascriptで操作できるようにしたりするわけです。
この時の問題の1つがインデントです。
あるプロジェクトではスペース3個ずつのインデントをしていたと思えば、別のプロジェクトではタブ1個ずつのインデントになっていたりしますし、同じプロジェクトでもhtmlはスペースインデントだけどjsはタブインデントだったりして、その組み合わせがまたプロジェクトごとに違ったりするわけです。
個人的な好みでテキストエディタにはEmacsを使っているのですが、最近になってようやく「プロジェクトごとにインデントを切り替える」ということを簡単にする設定が整理できたので、今回は他にいくつかの細かいTIPSも交えて、これを紹介したいと思います。
対象はとりあえずWindowsのEmacs24.3( こちら からダウンロードできます)とUbuntu 14.04のemacs24としますが、多分、他のLinuxディストリビューションやOSXでも同じ設定で動作すると思います。
Windows版については、環境変数で適当にホームディレクトリを設定しているとします。
~/.emacs
では最初に、基本的な設定を記述した~/local/conf/emacs.el
をロードしています。
(load "~/local/conf/emacs")
読み込んでいる~/local/conf/emacs.el
には滅多に変更しない変更を書いており、リポジトリで管理しています。
逆に ~/.emacs
の残りの部分では、あとで説明しますがその時々で頻繁に変わる設定を書いているため、リポジトリ管理しません。
~/local/conf/emacs.el
は以下のような内容になっています。
;; Load settings
(add-to-list 'load-path "~/local/common")
(mapcar
(lambda (path)
(add-to-list 'load-path path))
(directory-files "~/opt/" t "^[^\.]"))
(load "conf/emacs/backup")
(load "conf/emacs/global")
(load "tabbar")
(load "conf/emacs/tabbar")
(load "conf/emacs/color-theme")
(load "conf/emacs/windows")
(load "conf/emacs/lisp")
(load "conf/emacs/html")
(load "conf/emacs/scss")
(load "conf/emacs/js")
(load "conf/emacs/ruby")
2行目のadd-to-list
ですが、手元の環境では大まかなカテゴリーごとに分割した設定ファイルを ~/local/common/conf/emacs/XXX.el
として配置しており、これらのファイルを(load "conf/emacs/XXX")
のようにlaod
できるようにしています。
直接~/local/common/conf/emacs
までをadd-to-list
していないのは、設定ではなく同名のライブラリのelファイルと名前が被っている場合があるためで、設定ファイルの方にはconf/emacs
をつけることで区別するようにしています。
実際、上記の例ではライブラリのtabbar
のload
と、それ用に自分で書いた設定ファイルのconf/emacs/tabbar
のload
が混在しています。
3行目からのmapcar
では、~/opt/XXX
とか~/opt/YYY
などの、~/opt/
の下に配置している全サブディレクトリをload-path
にadd-to-list
しています。
このディレクトリには、emacsに標準添付されていないライブラリをそれぞれ専用のサブディレクトリを作って入れており(実際にはリポジトリからのチェックアウトやcloneです)、それらの~/opt/XXX/foo.el
等のライブラリファイルを(load "foo")
としてロードできるようにしています。
また、ファイル1個だけをダウンロードしてきたようなライブラリは同じ方法でload
できるように、~/opt/misc/
の下に入れています。
手元の環境では以下のようになっています。
opt/ ├── misc │ ├── feature-mode.el │ ├── rhtml-minor-mode.el │ ├── tabbar.el │ └── two-mode-mode.el └── scss-mode ├── .git ├── .gitignore ├── README.org └── scss-mode.el
8行目のconf/emacs/backup
からconf/emacs/windows
までは、バックアップファイルの作り方の設定やWindwos環境用のフォント設定などが入っていますが今回は省略します。
16行目でlaod
しているconf/emacs/html
が、htmlファイルに対するインデントの設定になります。
その前の15行目でもconf/emacs/lisp
でelispファイルに対するインデントの設定を書いているのですが、こちらは単に「インデントにはタブを使わない」としか書いていないのでやはり省略します(いつかの日か、elispを編集するお仕事を引き受けるときが来たら、このファイルにも改良が必要になるでしょう)。
さて、conf/emacs/html.el
はhtmlファイルに対するインデント設定で、以下のようになっています。
(defun setting-html-tab ()
(interactive)
(let* ((tab-width 3)
(hook `(lambda ()
(custom-set-variables
'(tab-width ,tab-width)
'(indent-tabs-mode t)
'(sgml-basic-offset ,tab-width)))))
(remove-hook 'html-mode-hook hook)
(add-hook 'html-mode-hook hook t)))
(defun setting-html-space (width &optional tab-width)
(interactive "nWidth:")
(let* ((tab-width (if tab-width tab-width 8))
(hook `(lambda ()
(custom-set-variables
'(tab-width ,tab-width)
'(indent-tabs-mode nil)
'(sgml-basic-offset ,width)))))
(remove-hook 'html-mode-hook hook)
(add-hook 'html-mode-hook hook t)))
このファイルでは2つの関数setting-html-tab
とsetting-html-space
を定義しています。
setting-html-tab
はインデントをタブで行う設定を行う関数で、M-x
でも実行できるようにinteractive
にしています。
変数hook
にlambda
を定義するときに、バッククオート(`
)でエスケープし、tab-width
変数を参照するときにカンマ(,
)を付けることで値を展開するようにしています。
こうしないと、hook
にはtab-width
が変数シンボルのままで与えられるため、フック実行時にはtab-width
変数に同じtab-width
変数を代入する(結果として何もしない)という事になってしまうからです。
また、変数名を変えて(しかし値展開せずに)hook
を定義した場合には、
Error setting sgml-basic-offset: (void-variable tmp-width)
といったエラーになってしまいます。
これは、例えばRubyやJavascriptだと、いわゆるブロックとかコールバック関数はローカル変数も含めたコンテキストで実行できるのですが、Emacsのlispでは定義した時のローカル変数がコンテキストに含まれないためです。
Emacs-24で新しく追加されたlexical-bindingを使うとそのように実行させる事も可能なのですが、Emacs-23な環境を使う場合があるため、このような形になっています。
hook
を定義したlet
の中では、一度remove-hook
をしてから、append
フラグにt
を指定してadd-hook
します。
これは、作業の途中で一時的にsetting-html-space
関数などでスペースインデント用のフックをadd-hook
してから再び戻すときのためで、remove-hook
しなければタブインデント用のフックがフック列の最後に登録し直されない(add-hook
は登録済みのフックが再登録された場合は何もしない)ためです。
2つ目の関数setting-html-space
では、インデントをスペースで行う設定を行う関数です。
(setting-html-space 3 6)
という使い方をして、1つ目の引数でインデント1段分のスペースの数を、2つ目の引数でタブ幅を指定します。
タブ幅は省略可能で、その場合は8が設定されます。
1つのファイル中のインデントは大抵の場合タブかスペースで統一されていますが、たまにタブとスペースを混在させたインデントが使われる場合があり、さらに想定しているタブ幅が8以外という事もあったりします。
そのようなファイルに手を加える場合に備えて、スペースインデントでもタブ幅だけは指定できるようにしています。
ここで紹介したのはhtml用の設定を行う関数の定義ですが、ほかにもcss(scss-modeで代用しています)やJavascript用の設定を行う関数も一通り用意しておきます。
実際のインデント設定は~/.emacs
でそれらの関数を適当な引数で実行することで行います。
先ほど先頭しか書かなかった~/.emacs
は実際にはこんな風になっています。
(load "~/local/conf/emacs")
(defun settings-foo ()
(interactive)
(setting-html-space 2 4)
(setting-css-space 4 4)
(setting-js-space 4 4))
(defun settings-bar ()
(interactive)
(setting-html-space 2)
(setting-css-tab)
(setting-js-tab))
;; (setting-ruby-tab)
;; (setting-scss-tab)
;; (setting-html-tab)
(settings-bar)
前半で定義している2つの関数は、特定のプロジェクト用にインデント設定関数の組み合わせをまとめたものです。
interactive
にしているので、途中で一時的に以前のプロジェクトのファイルを編集する必要があるときにも、M-x
で関数を呼び出すだけで反映されます。ただし、すでに開いているファイルに対して新しいインデント設定を反映されるためには、M-x
でそのファイル用のモードに切り替えなおす必要があります(htmlを編集中にインデントだけ別の方法に変えたときには、(すでにhtml-mode
になっている状態で改めて) M-x html-mode
と実行します)。
これで、「タブ・スペース混じりのインデントで書け」という指示がない限りは大抵のインデントに一発で対応できます。