読者です 読者をやめる 読者になる 読者になる

emacsの設定ファイル

emacsの設定ファイルについて考えてみる。(code-block.el)

便利な機能や設定を追加していくと次第にemacsの設定は膨れ上がっていく。 問題は以下の通り

  • どこに何の設定を書いたのか分からない。
  • 全体の依存関係が分からない。

どこに何の設定を書いたのか分からない。

例えば、全体で3000行くらいの設定ファイルがあるとする。 ここで、ある言語(e.g. python)用のある機能について(e.g. インデント)の設定を書き換えていたのだが、なぜかうまく行かない。 変更した設定が適用されてない。探してみると、変更を加えた箇所とは違う部分に同じ機能に対する設定が書かれておりそれにより上書きされてしまっていた。

おおげさに言うとこういう話。

全体の依存関係が分からない。

大変便利な機能として幅広い箇所で利用していた機能(拡張)の代替版が出た。どうやら、今使っている拡張の不満に思っていた点も改善されていて、より便利になっているらしい。 しかし、現在使っている拡張を利用している部分が設定ファイル中のそこかしこに散在している。 置き換えるにしても、すべてを駆逐するのは大変面倒な作業になりそう。 一方で、現状は、どうやら便利らしいという「噂」を聞いただけの段階で本当に置き換える価値があるのか判断を計り兼ねているというのが本当のところ。

以上のようなケースで、コピペを繰り返したような一枚岩の設定ファイルを目の前にし途方にくれるという感じになってしまう。 これをどうにかできないかと、たまに考えてみたりなどしたりする。

今回もそんな感じで考えてみることにした。

欲しい機能

欲しい機能は大雑把に言えば、閲覧性の高く取り外しのし易い設定ファイルということ。

そして意外とemacslispの持っている以下の性質が意外と便利に作用するのではないかと思った。

  • emacslispは関数内の関数定義がグローバルの関数定義
  • 関数の中で束縛していない変数についてsetqで代入してもグローバル変数な定義になる。

つまりこういうこと。

(defun function-f ()
  (setq this-is-global-variable t)
  (defun function-g () nil)
  )

(fboundp 'function-f) ; => t
(fboundp 'function-g) ; => nil
this-is-global-variable ; => t

(function-f)
(fboundp 'function-g) ; => t

関数定義で包めば中の処理を遅延させることができる。中に書いた設定を有効にしたければ、定義した関数を実行してあげれば良い。

あと、他にも以下のような便利そうな性質がある。

  • あと、関数定義の戻り値は関数名のシンボル
  • 関数を定義したファイル位置はfind-functionで探すことができる

こういうような。

(setq x (defun function-h () nil))
(eq x 'function-h) ; => t

;; M-x find-function function-h

もちろん、マクロを使えばもっと宣言的にしたりすることは可能だけれど。 関数定義と同時に、依存関係などを登録というようなことも気軽にできそう。

blue print

というわけで、設定ファイルをコードの断片として記述しておき、その断片を関数定義でくるめば後で索引可能になると思ったりした。 あとは依存関係を定義できれば良い訳で。以下のような形式で設定を書けるようにすれば良いのではないかと思った。

(config-block!
 nil
 (defun foo:base-settings ()
   (setq this-is-base-settings t)))

(config-block!
 '(core:utilities) ;; core:utilitiesは定義済み
 (defun foo:utilities ()
   """ utilities for foo"""
   (defun util0 () nil)
   (defun util1 () nil)
   (defun util2 () nil)
   (defun util3 () nil)))

(config-block!
 '(foo:utilities foo:base-settings)
 (defun foo:useful-addon-A ()
   (setq this-is-great-functionality t)
   ))

(config-block-setup!
 '(foo:useful-addon-A
   ;;foo:useful-addon-B
   foo:useful-addon-C
   ))

設定用の断片をconfig-block!で包む。これの第一引数は依存する断片の名前を書く。 このような断片をつなげていき、大きな機能になりそうな名前の設定を書く。 最後に各モードなどについて、config-block-setup!の中に利用したい機能を列挙する。

config-block-setup!は依存関係を考慮してconfig-block!で定義したコードの断片実行していく。 このようにすると

  • 関数定義なので索引が自動で作成される
  • 依存関係も考慮される
  • 不要な機能はconfig-block-setup!の時点でコメントしてあげれば良い

という感じに綺麗な設定ファイルを作ることができるかもしれないと思った。

結局

作った。 https://gist.github.com/3917706