GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Recursive-Editing.html ・ emacs-doc.conao3.com

22.13 再帰的編集

Emacsのコマンドループは、Emacsの起動時に自動的に入ります。このコマンドループの最上位での呼び出しは決して終了せず、Emacsが動作している限り実行され続けます。Lispプログラムからもコマンドループを呼び出すことができます。これによりコマンドループが複数回起動することになるため、これを再帰的編集と呼びます。再帰的編集レベルは、それを呼び出したコマンドを一時停止させ、ユーザーが任意の編集を行った後にそのコマンドを再開させるという効果を持ちます。

再帰的編集中に利用可能なコマンドは、トップレベルの編集ループで利用可能で、キーマップで定義されているものと同じです。再帰的編集レベルを抜ける特殊なコマンドはごく一部であり、それ以外のコマンドは処理が完了すると再帰的編集レベルに戻ります。(レベルを抜けるための特殊コマンドは常に利用可能ですが、再帰的編集が進行していないときは何も行いません。)

再帰的なループを含むすべてのコマンドループでは、コマンドループから実行されるコマンドでエラーが発生してもループを終了しないよう、汎用的なエラーハンドラが用意されています。

ミニバッファ入力は、特殊な形式の再帰的編集です。ミニバッファやミニバッファウィンドウの表示を可能にするなど、いくつか特有の仕様がありますが、その数は想像しているほど多くはありません。ミニバッファ内では特定のキーの挙動が異なりますが、それは単にミニバッファ独自のローカルマップによるもので、ウィンドウを切り替えれば、通常のEmacsコマンドが利用可能です。

再帰的な編集レベルを呼び出すには、関数 recursive-edit を呼び出します。この関数にはコマンドループが含まれており、さらにタグ exit を持つ catch への呼び出しも含まれています。これにより、exit にスローすることで再帰的な編集レベルを抜けることが可能になります(明示的な非ローカル脱出: catch と throw を参照)。t の値をスローすると recursive-edit が終了し、制御は1つ上のレベルのコマンドループに戻ります。これは アボート と呼ばれ、C-] (abort-recursive-edit) によって行われます。同様に、文字列値をスローして recursive-edit にエラーを発生させ、その文字列をメッセージとして出力させることもできます。関数をスローした場合、recursive-edit は引数なしでその関数を呼び出してから戻ります。それ以外の値をスローすると、recursive-edit は呼び出し元の関数に通常通り戻ります。コマンド C-M-c (exit-recursive-edit) がこれを行います。

ほとんどのアプリケーションでは、ミニバッファの利用の一環として行う場合を除き、再帰的な編集を行うべきではありません。通常は、現在のバッファのメジャーモードを一時的に特殊なメジャーモードに変更し、そこから前のモードに戻るためのコマンドを用意しておく方が、ユーザーにとって便利です。(Rmailのeコマンドはこの手法を採用しています。)あるいは、ユーザーに再帰的に編集させる別のテキストを提供したい場合は、特殊なモードで新しいバッファを作成して選択してください。そのモードで、処理を完了して前のバッファに戻るためのコマンドを定義します。(Rmailのmコマンドはこの仕組みになっています。)

再帰的な編集はデバッグに有用です。関数定義の中に一種のブレークポイントとして debug の呼び出しを挿入しておけば、その関数に到達した際に周辺を確認できます。debug は再帰的な編集を呼び出すと同時に、デバッガのその他の機能も提供します。

再帰的な編集レベルは、query-replace 内で C-r と入力したり、C-x q (kbd-macro-query) を使用したりする場合にも用いられます。

コマンド: recursive-edit

この関数はエディタのコマンドループを呼び出します。ユーザーが編集を開始できるよう、Emacsの初期化時に自動的に呼び出されます。Lispプログラムから呼び出された場合は、再帰的な編集レベルに入ります。

現在のバッファが選択中のウィンドウのバッファと異なる場合、recursive-edit は現在のバッファを保存し、復元します。そうでない場合、バッファを切り替えると、recursive-edit が戻った後に切り替わったバッファが現在のバッファとなります。

以下の例では、関数 simple-rec がまずポイント1を1単語分進め、次に再帰的な編集に入り、エコー領域にメッセージを出力します。その後、ユーザーは任意の編集を行い、C-M-c と入力して終了し、simple-rec の実行を継続できます。

(defun simple-rec ()
  (forward-word 1)
  (message "Recursive edit in progress")
  (recursive-edit)
  (forward-word 1))
     ⇒ simple-rec
(simple-rec)
     ⇒ nil
コマンド: exit-recursive-edit

この関数は、最内側の再帰的な編集(ミニバッファ入力を含む)から抜けます。その定義は実質的に (throw 'exit nil) です。

コマンド: abort-recursive-edit

この関数は、再帰的編集を終了した後に quit をシグナルすることで、最内側の再帰的編集(ミニバッファ入力を含む)を要求したコマンドを中断します。その定義は実質的に (throw 'exit t) となります。終了 を参照してください。

コマンド: top-level

この関数はすべての再帰的な編集レベルを終了します。いかなる計算からも完全に抜け出して直接メインのコマンドループに戻るため、値は返しません。

関数: recursion-depth

この関数は、再帰的な編集の現在の深度を返します。再帰的な編集が行われていない場合は、0を返します。