マクロ define-minor-mode
を使えば、単一の自己完結的な定義でモードを実装できる便利な方法が提供されます。
このマクロは、mode(シンボル)という名前の新しいマイナーモードを定義します。また、マイナーモードを切り替えるためのmodeというコマンドを定義し、そのドキュメント文字列としてdocを指定します。
toggleコマンドは、1つのオプション(プレフィックス)引数を取ります。引数なしで対話的に呼び出された場合は、モードのオン・オフを切り替えます。正のプレフィックス引数を指定するとモードが有効になり、それ以外のプレフィックス引数を指定すると無効になります。Lispでは、toggleという引数でモードを切り替え、省略またはnilという引数でモードを有効にします。これにより、例えばメジャーモードフック内でマイナーモードを簡単に有効にすることができます。docがnilの場合、このマクロは上記の内容を説明するデフォルトのドキュメント文字列を提供します。
デフォルトでは、mode という名前の変数も定義されており、モードの有効化・無効化によって t または nil に設定されます。
keyword-argsは、キーワードとそれに対応する値で構成されています。いくつかのキーワードには特別な意味があります:
:global globalnil
以外の場合、マイナーモードをバッファローカルではなくグローバルにすることを指定します。デフォルトは nil です。
マイナーモードをグローバルに設定することによる効果の一つに、mode
変数がカスタマイズ変数になることが挙げられます。Customize
インターフェースでこの変数を切り替えることでモードのオン・オフが切り替わり、その値は次回以降の Emacs セッションでも保存されます(The GNU Emacs Manual の Saving
Customizations を参照してください)。保存された変数を機能させるには、Emacs
の起動時に毎回マイナーモードの関数が利用可能であることを確認しておく必要があります。通常、これは define-minor-mode フォームを autoload としてマークすることで行われます。
:init-value init-valueこれは変数 mode に初期化される値です。特殊な状況(後述)を除き、この値は nil でなければなりません。なお、define-minor-mode
では、この値に従ってモードが実際に有効になっているかを確認するために、マイナーモードの本体が自動的に実行されるわけではありません。そのため、モードがグローバル(前述)であり、初期値が
nil 以外である場合は、次のようにモードの読み込み時に Emacs
にモード関数を強制的に実行させることを検討してください。
:initialize #'custom-initialize-after-file-load
そうでないと、マイナーモードが適切に設定されていないにもかかわらず、有効であると表示してしまう可能性があります。
:lighter lighter文字列 lighter は、モードが有効なときにモードラインに何を表示するかを指定します。nil の場合は、モードはモードラインに表示されません。
:keymap keymapオプション引数 keymap は、マイナーモードのキーマップを指定します。nil
以外の場合は、変数名(その値がキーマップとなるもの)、キーマップ、または次のような形式のアリストである必要があります。
(key-sequence . definition)
ここで、各 key-sequence および definition は define-key
に渡すのに適した引数です(キーバインドの変更を参照)。keymap がキーマップまたはアリストである場合、これにより変数 mode-map も定義されます。
:variable placeこれは、モードの状態を格納するために使用されるデフォルト変数 mode
を置き換えます。これを指定すると、変数 mode は定義されず、init-value 引数は使用されません。place
には、別の名前の変数(自身で定義する必要があります)、または setf
関数で使用可能な任意の値(一般化変数
を参照)を指定できます。place にはコンス (get . set) を指定することもでき、その場合
get は現在の状態を返す式、set
は単一の引数(状態)を取る関数となり、これを place に代入する必要があります。
:after-hook after-hookこれは、モードフックの実行後に評価される単一のLispフォームを定義します。クォートしてはいけません。
:interactive valueマイナーモードは、デフォルトでは対話型コマンドです。value が nil である場合、この動作は抑制されます。value
がシンボルのリストである場合、そのマイナーモードがどのメジャーモードで有用であるかを示すために使用されます。
その他のキーワード引数は、変数 mode 用に生成された defcustom に直接渡されます。これらのキーワードとその値の説明については、カスタマイズ変数の定義 を参照してください。
mode という名前のコマンドは、まず mode
という名前の変数を設定するといった標準的な処理を行い、次に body
のフォームがあればそれを実行します。続いてモードフック変数 mode-hook を実行し、最後に :after-hook
内の任意のフォームを評価して終了します。(なお、フックの実行を含むこれらすべての処理は、モードが有効な場合と無効な場合の両方で行われます。)
初期値は nil でなければなりませんが、(1) Emacs
にモードがプリロードされている場合、または (2)
ユーザーが要求していなくても、モードを有効にするためのロードが支障ない場合はこの限りではありません。例えば、他の何かが有効にならない限りそのモードが機能せず、その時点では必ずロードされるのであれば、デフォルトで有効にしても問題はありません。しかし、これらは特殊な状況です。通常、初期値は
nil でなければなりません。
define-minor-mode の使用例を以下に示します。
(define-minor-mode hungry-mode "Toggle Hungry mode. Interactively with no argument, this command toggles the mode. A positive prefix argument enables the mode, any other prefix argument disables it. From Lisp, argument omitted or nil enables the mode, `toggle' toggles the state. When Hungry mode is enabled, the control delete key gobbles all preceding whitespace except the last. See the command \\[hungry-electric-delete]." ;; The initial value. nil ;; The indicator for the mode line. " Hungry" ;; The minor mode bindings. '(([C-backspace] . hungry-electric-delete)))
ここでは、「Hungry mode」という名称のマイナーモードと、それを切り替えるためのhungry-modeというコマンド、モードが有効かどうかを示すhungry-modeという変数、およびモード有効時に適用されるキーマップを保持するhungry-mode-mapという変数を定義しています。また、C-DELのキーバインドを用いてキーマップを初期化します。body形式は存在しません—多くのマイナーモードでは不要なためです。
別の書き方はこちらです:
(define-minor-mode hungry-mode
"Toggle Hungry mode.
...rest of documentation as before..."
;; The initial value.
:init-value nil
;; The indicator for the mode line.
:lighter " Hungry"
;; The minor mode bindings.
:keymap
'(([C-backspace] . hungry-electric-delete)
([C-M-backspace]
. (lambda ()
(interactive)
(hungry-electric-delete t)))))
これは、すべての(あるいは一部の;後述)バッファにおいてバッファローカルなマイナーモード mode を有効化または無効化する、global-mode という名前のグローバルトグルを定義します。また、body フォームを実行します。バッファでマイナーモードを有効化するには関数 turn-on を使用し、無効化するには引数に −1 を指定して mode を呼び出します。(関数 turn-on は独立した関数であるため、常に有効化すべきかどうかがあらかじめ明確でない場合に、マイナーモードを有効化するかどうかを判定できます。)
なお、これは :global キーワード non-nil で define-minor-mode
を使用する場合とは異なります。このマクロで定義されるモードは依然としてバッファローカルであり、単にすべての(あるいは一部の)バッファで有効になるだけです。
グローバルにモードを有効にすると、その後に作成されるバッファのうち、run-mode-hooks
を実行するという規約に従うメジャーモードを使用するものにのみ影響が及びます。この規約に従わないメジャーモードでは、マイナーモードは有効になりません。
このマクロは、カスタマイズオプション global-mode(カスタマイズ設定を参照)を定義します。これは Customize
インターフェースを通じて切り替えることで、マイナーモードのオン・オフを切り替えることができます。define-minor-mode と同様に、例えば :require キーワードを指定するなどして、Emacs の起動時に毎回 define-globalized-minor-mode フォームが評価されるようにしてください。
keyword-args で :group group を使用し、グローバルマイナーモードのモード変数用のカスタムグループを指定します。
デフォルトでは、モードのオン・オフ状態を示すバッファローカルなマイナーモード変数は、モード名そのものと同じ名前になっています。そうでない場合は
:variable variable
を使用してください。マイナーモードによっては、この状態情報を格納するために別の変数を使用する場合があります。
一般的に、グローバルに適用されるマイナーモードを定義する際は、個々のバッファで利用(あるいは無効化)できるよう、グローバルではないバージョンも併せて定義すべきです。これにより、特定のメジャーモード内でグローバルに有効になっているマイナーモードを、そのモードのフックを用いて無効化することも可能になります。
マクロに :predicate キーワードが指定されると、グローバルモード変数と同名で、末尾が
-mode ではなく -modes
となるユーザーオプション(すなわち global-modes)が作成されます。この変数は、特定のメジャーモードでマイナーモードを有効にするかどうかを判定する述語関数で使用され、ユーザーはこの変数の値をカスタマイズすることで、マイナーモードが切り替わるモードを制御できます。:predicate
の有効な値(したがって、それによって作成されるユーザーオプションの有効な値)には、t(すべてのメジャーモードで使用)、nil(どのメジャーモードでも使用しない)、またはモード名のリスト(任意で not を前置可能。例:(not mode-name …))が含まれます。これらの要素は、以下の例に示すように組み合わせて使用できます。
(c-mode (not mail-mode message-mode) text-mode)
これは、「c-modeから派生したモードでは使用し、message-modeやmail-modeから派生したモードでは使用せず、text-modeから派生したモードでは使用し、それ以外のモードでは使用しない」という意味です。
((not c-mode) t)
これは、「c-modeから派生したモードでは使用せず、それ以外の場所ではすべて使用する」という意味です。
(text-mode)
これは「text-modeから派生したモードでは使用するが、それ以外では使用しない」という意味です。(末尾には暗黙のnil要素があります。)
マイナーモードでは、Emacsの一部の機能に影響を与えるバッファローカル変数が設定されることがよくあります。マイナーモードをオフにすると、これらの変数の以前の状態が復元されることが期待されます。この便利なマクロはその処理を支援します。setq-localとほぼ同様に動作しますが、これらの値を以前の値や状態に復元するために使用できるオブジェクトを返します(付随する関数
buffer-local-restore-state を使用します)。