これまでの節では、defcustom
に対する精緻な型仕様の構築方法について説明してきました。場合によっては、このような型仕様に名前を付けたいこともあるでしょう。明らかな例は、多くのユーザーオプションに同じ型を使用する場合です。オプションごとに仕様を繰り返すのではなく、型仕様に名前を付け、各
defcustom
でその名前を使用することができます。もう一つの例は、ユーザーオプションの値が再帰的なデータ構造である場合です。データ型が自身を参照できるようにするには、その型に名前を付ける必要があります。
カスタムタイプはウィジェットとして実装されているため、新しいカスタムタイプを定義するには、新しいウィジェットを定義します。ここではウィジェットのインターフェースについて詳細は説明しませんので、その点についてはThe Emacs Widget LibraryのIntroductionを参照してください。代わりに、簡単な例を用いて、新しいカスタムタイプを定義するために必要な最小限の機能を実演します。
(define-widget 'binary-tree-of-string 'lazy
"A binary tree made of cons-cells and strings."
:offset 4
:tag "Node"
:type '(choice (string :tag "Leaf" :value "")
(cons :tag "Interior"
:value ("" . "")
binary-tree-of-string
binary-tree-of-string)))
(defcustom foo-bar ""
"Sample variable holding a binary tree of strings."
:type 'binary-tree-of-string)
新しいウィジェットを定義するための関数は define-widget
と呼ばれます。第1引数は、新しいウィジェット型として定義したいシンボルです。第2引数は既存のウィジェットを表すシンボルであり、新しいウィジェットはこの既存ウィジェットとの差分に基づいて定義されます。新しいカスタマイズ型を定義する目的では、lazy ウィジェットが最適です。なぜなら、同名の defcustom のキーワード引数と同じ構文を持つ :type
キーワード引数を受け付けるからです。第3引数は、新しいウィジェットのドキュメント文字列です。この文字列は M-x
widget-browse RET binary-tree-of-string RET コマンドで確認できます。
これらの必須引数の後に、キーワード引数が続きます。最も重要なのは :type
で、これはこのウィジェットで照合したいデータ型を指定するものです。ここでは binary-tree-of-string が、文字列か、あるいは car と cdr がともに binary-tree-of-string である cons-cell
のいずれかとして指定されています。現在定義を進めているウィジェット型への参照がある点に注意してください。:tag も重要なキーワード引数です。というのも、新しいウィジェットに lazy ウィジェットを使用しているからです。デフォルトでは lazy
ウィジェットにタグは設定されておらず、タグがない場合、カスタマイズバッファにはウィジェットの値全体(つまり、カスタマイズ対象となっているユーザーオプションの値)が表示されます。これはほとんどの場合好ましくないため、binary-tree-or-string ウィジェットに名前を付けるための文字列を指定します。:offset
引数は、子ノードが親ノードに対して4スペース分インデントされるようにし、カスタマイズバッファでツリー構造を明確に表示させるためのものです。
defcustomは、新しいウィジェットを通常のカスタマイズタイプとしてどのように利用できるかを示しています。
lazy
という名称の理由は、バッファ内でウィジェットがインスタンス化される際、他の複合ウィジェットが下位ウィジェットを内部形式に変換するためです。この変換は再帰的に行われるため、下位ウィジェットは自身の下位ウィジェットを変換します。データ構造自体が再帰的な場合、この変換は無限再帰となります。lazy ウィジェットはこの再帰を防ぎ、必要な場合にのみ引数の :type を変換します。