最も単純な形態において、フレームは常にバッファの表示に使用できる単一のウィンドウを保持します。その結果、そこにバッファを配置することに成功するのは、常に直近の
display-buffer の呼び出しとなります。
このようなフレームでの作業はあまり実用的ではないため、Emacsではデフォルトで、フレームサイズおよびsplit-height-thresholdとsplit-width-thresholdオプションの既定値によって制御される、より複雑なフレームレイアウトが利用可能です。まだ表示されていないバッファをフレームに表示する場合、そのフレーム上の単一ウィンドウを分割するか、あるいは2つのウィンドウのうち一方を(再)使用します。
ユーザーがこれらの閾値のいずれかをカスタマイズしたり、フレームのレイアウトを手動で変更したりした時点で、デフォルトの動作は適用されなくなります。また、nil 以外の action 引数を指定して display-buffer
を呼び出した場合や、ユーザーが前述のサブセクションで挙げたオプションのいずれかをカスタマイズした場合にも、デフォルトの動作は適用されなくなります。適用可能な表示アクションやそれに伴うフレームレイアウトが非常に多岐にわたるため、display-buffer を習得することは、すぐにストレスの溜まる体験になるかもしれません。
しかし、バッファ表示機能の使用を控え、ウィンドウの分割・削除というメタファーに頼ることも得策ではありません。バッファ表示機能は、Lispプログラムとユーザーがそれぞれの異なるニーズを調整するための枠組みを提供しますが、ウィンドウの分割・削除に関しては、それに匹敵する枠組みは存在しません。また、バッファ表示機能を利用すれば、後でバッファをフレームから削除した際に、少なくとも部分的にフレームのレイアウトを復元することが可能です(ウィンドウの終了を参照)。
以下に、前述の不満を解消し、フレームのウィンドウ間で文字通りバッファを失う事態を避けるためのガイドラインをいくつか提示します。
表示アクションの記述は、アクション関数とアクションアリスタを一つの巨大なリストにまとめる必要があるため、手間がかかることがあります。(歴史的な経緯により、display-buffer
でこれらに対する個別の引数をサポートすることはできませんでした。)以下に挙げるような基本的な形式をいくつか覚えておくと役立つかもしれません:
'(nil (inhibit-same-window . t))
アクションのalistエントリのみを指定し、アクション関数は指定しません。その唯一の目的は、別の場所で指定されたdisplay-buffer-same-window関数が、同じウィンドウにバッファを表示するのを抑制することです。前節の最後の例も参照してください。
'(display-buffer-below-selected)
一方で、1つのアクション関数と空のアクションalistを指定します。上記の2つの指定の効果を組み合わせるには、次のような形式を記述します。
'(display-buffer-below-selected (inhibit-same-window . t))
別のアクション関数を追加するには、次のように記述します。
'((display-buffer-below-selected display-buffer-at-bottom) (inhibit-same-window . t))
また、alistに別のエントリを追加するには、次のように記述します。
'((display-buffer-below-selected display-buffer-at-bottom) (inhibit-same-window . t) (window-height . fit-window-to-buffer))
最後の形式は、次のように display-buffer の action 引数として使用できます。
(display-buffer (get-buffer-create "*foo*") '((display-buffer-below-selected display-buffer-at-bottom) (inhibit-same-window . t) (window-height . fit-window-to-buffer)))
display-buffer-alist のカスタマイズでは、次のように使用します。
(setopt
display-buffer-alist
'(("\\*foo\\*"
(display-buffer-below-selected display-buffer-at-bottom)
(inhibit-same-window . t)
(window-height . fit-window-to-buffer))))
2つ目のバッファにカスタマイズを追加するには、次のように記述します。
(setopt
display-buffer-alist
'(("\\*foo\\*"
(display-buffer-below-selected display-buffer-at-bottom)
(inhibit-same-window . t)
(window-height . fit-window-to-buffer))
("\\*bar\\*"
(display-buffer-reuse-window display-buffer-pop-up-frame)
(reusable-frames . visible))))
display-buffer-alist と display-buffer-base-action
はユーザーオプションであり—Lispプログラムがこれらを設定したり再バインドしたりしてはなりません。一方、display-buffer-overriding-action
はアプリケーション用に予約されており—このオプションを使用することは稀であり、使用する場合でも細心の注意を払う必要があります。
display-buffer の旧バージョンの実装では、pop-up-frames や pop-up-windows
といったユーザーオプションの設定を巡って、ユーザーとアプリケーションが競合することが頻繁にありました(バッファ表示用の追加オプション
を参照)。これが、ユーザーとアプリケーションに許可すべき動作を明確に規定するフレームワークを提供するため、display-buffer を再設計した主要な理由の一つとなりました。
Lispプログラムでは、ユーザーによるカスタマイズによってバッファが予期せぬ形で表示される可能性があることを考慮しておく必要があります。後続の動作において、バッファがdisplay-bufferのaction引数で指定した通りに正確に表示されたと決して想定してはなりません。
ユーザーは、任意のバッファの表示方法に対して、過度に多く、かつ厳しすぎる制限を設けるべきではありません。さもなければ、特定の目的でバッファを表示するという特性を損なう恐れがあります。例えば、2つのウィンドウでバッファの異なるバージョンを並べて比較するためのLispプログラムが作成されたとします。もしdisplay-buffer-alistのカスタマイズによって、そのようなバッファは常に選択されたウィンドウ内またはその下に表示されるよう規定されている場合、そのプログラムはdisplay-bufferを介して意図したウィンドウ構成を設定するのに苦労することになります。
任意のバッファを表示する設定を指定するには、ユーザーはdisplay-buffer-base-actionをカスタマイズする必要があります。複数のフレームでの操作を好むユーザーがどのように設定を行うかの例は、前のサブセクションで示しました。display-buffer-alistは、特定のバッファを特定の方法で表示するために予約しておく必要があります。
一般的に、ユーザーやLispプログラマは、ウィンドウに該当のバッファが既に表示されている場合に備え、そのウィンドウを再利用できるようにしておくのが常に望ましいと言えます。前述のサブセクションで示したように、これを適切に行わないと、当該バッファを表示するフレームが既に存在しているにもかかわらず、display-buffer
が新しいフレームを次々とポップアップさせる可能性があります。ごく一部のケースでは、例えばそのウィンドウにバッファの異なる部分を表示させたい場合など、ウィンドウの再利用が望ましくないこともあります。
したがって、display-buffer-reuse-window は、action の引数およびカスタマイズの両方において、可能な限り頻繁に使用すべきアクション関数の一つです。action 引数における inhibit-same-window
の指定は、通常、バッファを表示しているウィンドウの再利用を避けるべき最も一般的なケース――すなわち、当該ウィンドウが選択中のウィンドウである場合――に対応します。
複数のフレームを扱うユーザーにとって、これは考えるまでもないことです。バッファを表示するフレームは、inhibit-switch-frame
エントリで禁止されていない限り、自動的に最前面に表示され、フォーカスを獲得します。単一フレームのユーザーにとって、この作業はかなり困難な場合があります。特に、display-buffer-pop-up-window や display-buffer-use-some-window
はこの点において邪魔になることがあります。これらはウィンドウを分割したり、一見恣意的な(多くの場合、最大サイズのものや直近で使用されていないもの)ウィンドウを使用したりするため、ユーザーの注意を散漫にさせます。
そのため、一部のLispプログラムでは、例えばフレーム下部にウィンドウを配置することで、ユーザーが新しいウィンドウに関する質問に回答することを想定したミニバッファウィンドウの付近にバッファを表示させようとします。入力に関連しない操作の場合、選択されたウィンドウには通常すでにユーザーの注意が向けられているため、display-buffer-below-selectedの方が好ましい場合があります。
多くのアプリケーションでは、nil 以外の norecord 引数を指定して with-selected-window や select-window を呼び出した際に生成されるウィンドウ遷移の中から display-buffer
を呼び出しています。このような手法はほぼ常に好ましくありません。というのも、こうした遷移の中で選択されるウィンドウは、通常、ユーザーに提示される設定で選択されているウィンドウとは異なるためです。
例えば、ユーザーが inhibit-same-window という alist
エントリを追加していた場合、そのエントリはエクスカーションの範囲内で選択されたウィンドウを回避し、結果として得られた設定で選択されたウィンドウを回避したはずです。たとえそのようなエントリが追加されていなくても、結果としての挙動が不自然になる可能性があります。ライブウィンドウを1つ含むフレーム内で、以下のフォームを評価すると、
(progn (split-window) (display-buffer "*Messages*"))
下部に*Messages*バッファを表示するウィンドウを表示し、もう一方のウィンドウを選択したままにします。次のフォームを評価します
(with-selected-window (split-window) (display-buffer "*Messages*"))
上部のウィンドウに *Messages* が表示され、それを選択することになりますが、これは通常
display-buffer が意図している動作ではありません。
一方で、以下の形式を評価する際
(progn (split-window) (pop-to-buffer "*Messages*"))
*Messages* バッファを正しく選択し、次のフォームへ
(progn
(split-window)
(with-selected-window (selected-window)
(pop-to-buffer "*Messages*")))
しません。
また、選択されたウィンドウがすべてのウィンドウの中で最大の使用時間を持つことを前提とする display-buffer-use-some-window や display-buffer-use-least-recent-window
といったアクション関数の呼び出しでは、仕様通りにウィンドウが生成されない場合があります。
したがって、ウィンドウエクスカーションの利用を前提とするアプリケーションでは、display-buffer の呼び出しをエクスカーションが終了するまで延期するようにすべきです。