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

29.15.5 アクション関数の優先順位

前述のサブセクションから、バッファを表示するには display-buffer に複数の表示アクション(バッファ表示用のウィンドウの選択 を参照)を指定する必要があることが既に分かっています。完全にカスタマイズされていない Emacs では、これらのアクションは display-buffer-fallback-action によって、以下の優先順位で指定されます:ウィンドウの再利用、同一フレーム上への新しいウィンドウのポップアップ、以前にそのバッファを表示していたウィンドウの使用、あるウィンドウの使用と新しいフレームのポップアップ。(なお、display-buffer-fallback-action で指定される残りのアクションは、カスタマイズされていない Emacs では無効であることに注意してください。)

次の形式を考えます:

(display-buffer (get-buffer-create "*foo*"))

カスタマイズされていないEmacsセッションのバッファ *scratch* でこのフォームを評価すると、通常、既に *foo* を表示しているウィンドウの再利用は行われませんが、新しいウィンドウをポップアップさせることは可能です。同じフォームを再度評価しても、目に見える変化は生じません—display-buffer は、当該操作が適用可能であり、かつ適用可能なすべての操作の中で最優先であったため、既に *foo* を表示しているウィンドウを再利用しました。

選択したフレームに十分な空きスペースがない場合、新しいウィンドウを表示することはできません。カスタマイズされていないEmacsでは、通常、1つのフレームにすでに2つのウィンドウが存在する場合に失敗します。例えば、ここで C-x 1 と入力し、続けて C-x 2 と入力して再度フォームを評価すると、*foo* は下のウィンドウに表示されるはずです—display-buffer は単に「some」ウィンドウを使用しただけです。もし C-x 2 と入力する前に C-x o と入力していたなら、*foo* は上のウィンドウに表示されていたでしょう。なぜなら、「some」ウィンドウは「最後に使用されなかった」ウィンドウを指し、選択中のウィンドウが最後に使用されなかったのは、それがフレーム上に単独で存在する場合に限られるからです。

C-x o と入力せず、下のウィンドウに *foo* が表示されているとしましょう。そこに移動するために C-x o と入力し、続けて C-x left と入力して、再度フォームを評価してください。すると、同じ下のウィンドウに *foo* が表示されるはずです。というのも、そのウィンドウでは以前に *foo* が表示されていたため、他のウィンドウではなくそちらが選ばれたからです。

So far we have only observed the default behavior in an uncustomized Emacs session. To see how this behavior can be customized, let’s consider the option display-buffer-base-action. It provides a very coarse customization which conceptually affects the display of any buffer. It can be used to supplement the actions supplied by display-buffer-fallback-action by reordering them or by adding actions that are not present there but fit more closely the user’s editing practice. However, it can also be used to change the default behavior in a more profound way.

通常、バッファを別のフレームに表示することを好むユーザーを想定してみましょう。このようなユーザーは、次のようなカスタマイズを行うかもしれません。

(setopt
 display-buffer-base-action
 '((display-buffer-reuse-window display-buffer-pop-up-frame)
   (reusable-frames . 0)))

この設定により、display-buffer はまず可視またはアイコン化されたフレーム上でバッファを表示しているウィンドウを探し、そのようなフレームが存在しない場合は新しいフレームをポップアップさせます。グラフィカルなシステムでは、*scratch* を表示しているウィンドウで C-x 1 と入力し、我々の正準形式 display-buffer を評価することで、この挙動を確認できます。通常、これにより *foo* を表示しているルートウィンドウを持つ新しいフレームが作成され(かつフォーカスが当たります)、そのフレームをアイコン化して再度正準形式を評価すると、display-buffer は新しいフレーム上のウィンドウを再利用します(通常、フレームが最前面に表示され、フォーカスも当たります)。

新しいフレームの作成に失敗した場合に限り、display-buffer は display-buffer-fallback-action で指定されたアクションを実行します。つまり、再度ウィンドウの再利用を試みたり、新しいウィンドウをポップアップさせたりといった処理が行われます。フレーム作成を失敗させる簡単な方法として、以下の形式が挙げられます:

(let ((pop-up-frame-function 'ignore))
  (display-buffer (get-buffer-create "*foo*")))

そのフォームが新しいフレームを作成せず、代わりにフォールバックアクションを使用していることが確認でき次第、すぐにそのフォームのことは忘れます。

なお、display-buffer-base-action のカスタマイズにおいて display-buffer-reuse-window が冗長に見えるのは、それが既に display-buffer-fallback-action の一部であり、いずれにせよそこで試されるべきだからです。しかし、display-buffer-base-action が display-buffer-fallback-action より優先されるため、その時点では既に display-buffer-pop-up-frame が優先されてしまい、これでは失敗します。実際、こちらが:

(setopt
 display-buffer-base-action
 '(display-buffer-pop-up-frame (reusable-frames . 0)))

display-buffer が常に新しいフレームをポップアップさせることになり、おそらくそれはユーザーが望んでいる動作ではないでしょう。

ここまでは、ユーザーがdisplay-bufferのデフォルト動作をカスタマイズする方法についてのみ説明してきました。次に、アプリケーションがdisplay-bufferの挙動をどのように変更できるかを見てみましょう。その標準的な方法は、display-bufferのaction引数や、それを呼び出す関数(例えばpop-to-bufferなど)を使用することです(ウィンドウ内のバッファへの切り替えを参照してください)。

あるアプリケーションが *foo* を、できれば選択中のウィンドウの下(ユーザーの注意を即座に新しいウィンドウに向けるため)、あるいはそれが不可能な場合はフレーム下部のウィンドウに表示したいとします。これは、次のような呼び出しで実現できます:

(display-buffer
 (get-buffer-create "*foo*")
 '((display-buffer-below-selected display-buffer-at-bottom)))

この新しく修正された形式がどのように動作するかを確認するには、*foo* が表示されているフレームをすべて削除し、*scratch* が表示されているウィンドウに C-x 1 に続いて C-x 2 と入力し、その後その形式を評価してください。display-buffer によって上のウィンドウが分割され、新しいウィンドウに *foo* が表示されるはずです。あるいは、C-x 2 の後に C-x o と入力していた場合は、代わりに display-buffer によって下のウィンドウが分割されていたでしょう。

ここで、新しいフォームを評価する前に、例えばそのウィンドウでフォーム (fit-window-to-buffer) を評価するなどして、選択中のウィンドウを可能な限り小さくしたとしましょう。その場合、display-buffer は選択中のウィンドウを分割できず、代わりにフレームのルートウィンドウを分割することになり、結果として *foo* がフレームの下部に表示されることになります。

いずれの場合も、新しいフォームを再度評価する際は、*foo* が既に表示されているウィンドウを再利用するはずです。というのも、action 引数で指定された両方の関数が、まずそのようなウィンドウを再利用しようとするためです。

しかし、Lispプログラムにおいて、別の関数の背後に隠されたdisplay-buffer呼び出しのデフォルト動作を変更したい場合があります。そのような場合、action引数を設定するという選択肢はありません。幸いなことに、display-buffer-default-alist変数を使用して、条件付きのデフォルト動作のリストを指定できます。以下の例を考えてみましょう。

(defun foo (buffer)
   (display-buffer
    buffer
    '((display-buffer-below-selected display-buffer-at-bottom))))

(defun bar ()
   (let ((display-buffer-default-alist
         '(("\\*bar\\*" (display-buffer-pop-up-frame)))))
     (foo (get-buffer-create "*bar*"))))

(defun baz ()
   (let ((display-buffer-default-alist
         '(("\\*baz\\*" (display-buffer-same-window)))))
     (foo (get-buffer-create "*baz*"))))

ここでは、foo が特定の action を指定して display-buffer を呼び出します。Let 束縛 display-buffer-default-alist により、関数 bar と baz はそれぞれ独自のアクション関数を指定できます。

action 引数および/または display-buffer-default-alist 変数を設定することで、アプリケーションは実質的に display-buffer-base-action のあらゆるカスタマイズを上書きします。ユーザーは、アプリケーションによる選択をそのまま受け入れるか、あるいは以下のようにオプション display-buffer-alist をカスタマイズしてさらに対応することが可能です。

(setopt
 display-buffer-alist
 '(("\\*foo\\*"
    (display-buffer-reuse-window display-buffer-pop-up-frame))))

上記の新しく修正したフォームを用い、どこにも *foo* が表示されない設定で試すと、display-buffer の action 引数を完全に無視して、別のフレームに *foo* が表示されます。

なお、display-buffer-alist の仕様において、reusable-frames のアクションリスト項目を明示的に指定しなかった点にご留意ください。display-buffer は常に最初に見つかった項目(今回の場合は display-buffer-base-action で指定された項目)を採用します。例えば、*foo* を表示しているアイコン化されたフレームを再利用可能なリストから除外したい場合など、別の仕様を適用したい場合は、それを別途指定する必要があります:

(setopt
 display-buffer-alist
 '(("\\*foo\\*"
    (display-buffer-reuse-window display-buffer-pop-up-frame)
    (reusable-frames . visible))))

これを試してみると、*foo* を表示しようとする操作を繰り返しても、そのフレームが表示されている場合にのみ、フレームの再利用に成功することに気づくでしょう。

上記の例からは、ユーザーが display-buffer-alist をカスタマイズするのは、アプリケーションによって選択された action 引数を上書きするという唯一の目的のためであるという結論が導き出されるだろう。しかし、そのような結論は誤りである。display-buffer-alist は、表示が action 引数によっても制御されるか否かにかかわらず、ユーザーが特定のバッファの表示方法を好みの形で指定するための標準的なオプションである。

しかしながら、display-buffer-alist のカスタマイズは display-buffer-base-action のカスタマイズとは2つの主要な点で異なると合理的に結論付けられます。すなわち、display-buffer の action 引数を上書きするためより強力であること、そして影響を受けるバッファを明示的に指定できることです。実際、他のバッファの表示は *foo* のカスタマイズによって一切影響を受けません。例えば、

(display-buffer (get-buffer-create "*bar*"))

引き続きdisplay-buffer-base-actionおよびdisplay-buffer-fallback-actionの設定のみによって制御される。

例を挙げるのはここまでにしてもいいでしょうが、Lispプログラムには、display-buffer-alist のあらゆるカスタマイズを無効化できる切り札がまだあります。それが変数 display-buffer-overriding-action で、display-buffer の呼び出しを次のように囲むようにバインドできるのです:

(let ((display-buffer-overriding-action
       '((display-buffer-same-window))))
  (display-buffer
   (get-buffer-create "*foo*")
   '((display-buffer-below-selected display-buffer-at-bottom))))

このフォームを評価すると、action 引数やユーザーによるカスタマイズに関わらず、通常は選択中のウィンドウに *foo* が表示されます。(通常、アプリケーションがわざわざ action 引数まで用意することはありません。ここでは、それが上書きされるという点を示すために用いています。)

ここで提供したカスタマイズを適用した場合に、アクション関数 display-buffer が表示しようとしたであろう *foo* の一覧を確認すると、分かりやすいかもしれません。その一覧(誰がこれを追加したか、および後続の要素について説明するコメントを含む)は以下の通りです:

(display-buffer-same-window  ;; `display-buffer-overriding-action'
 display-buffer-reuse-window ;; `display-buffer-alist'
 display-buffer-pop-up-frame
 display-buffer-below-selected ;; ACTION argument
 display-buffer-at-bottom
 display-buffer-reuse-window ;; `display-buffer-base-action'
 display-buffer-pop-up-frame
 display-buffer--maybe-same-window ;; `display-buffer-fallback-action'
 display-buffer-reuse-window
 display-buffer--maybe-pop-up-frame-or-window
 display-buffer-in-previous-window
 display-buffer-use-some-window
 display-buffer-pop-up-frame)

なお、ここに挙げた内部関数のうち、display-buffer--maybe-same-windowは実質的に無視され、display-buffer--maybe-pop-up-frame-or-windowは実際にdisplay-buffer-pop-up-windowを実行します。

各関数呼び出しで渡されるアクションリストは以下の通りです:

((reusable-frames . visible)
 (reusable-frames . 0))

これは、上記で display-buffer-alist の2番目の指定を使用し、display-buffer-base-action で提供された指定を上書きしたことを示しています。仮にユーザーがそれを次のように記述していたとしましょう。

(setopt
 display-buffer-alist
 '(("\\*foo\\*"
    (display-buffer-reuse-window display-buffer-pop-up-frame)
    (inhibit-same-window . t)
    (reusable-frames . visible))))

この場合、inhibit-same-window のアリストエントリによって display-buffer-same-window の仕様が正常に無効化され、display-buffer-overriding-action と display-buffer は別のフレームで *foo* を表示することになります。この点において display-buffer-overriding-action の堅牢性を高めるには、アプリケーション側で適切な inhibit-same-window エントリも指定する必要があり、例えば以下のようになります。

(let ((display-buffer-overriding-action
       '(display-buffer-same-window (inhibit-same-window . nil))))
  (display-buffer (get-buffer-create "*foo*")))

この最後の例は、バッファ表示用のウィンドウの選択で説明されている通り、アクション関数の優先順位は固定されているものの、その順序で下位にランク付けされた表示アクションによって指定されたアクションアリストのエントリが、より上位にランク付けされた表示アクションの実行に影響を及ぼし得ることを示しています。