В своей простейшей форме фрейм всегда вмещает одно единственное окно, которое может использоваться для отображения буфера. Как следствие, именно последний вызов display-buffer окажется тем, которому удалось разместить там свой буфер.
Поскольку работать с таким фреймом не очень практично, Emacs по умолчанию допускает более сложные компоновки фреймов, управляемые значениями по умолчанию для размера фрейма и параметров split-height-threshold и split-width-threshold. Отображение буфера, еще не показанного во фрейме, тогда либо разделяет единственное окно в этом фрейме, либо (повторно) использует одно из его двух окон.
Поведение по умолчанию прекращается, как только пользователь настраивает один из этих порогов или вручную изменяет компоновку фрейма. Поведение по умолчанию также прекращается при вызове display-buffer с аргументом action, не равным nil, или когда пользователь настраивает один из параметров, упомянутых в предыдущих подразделах. Освоение display-buffer вскоре может стать разочаровывающим опытом из-за множества применимых действий отображения и получаемых в результате компоновок фреймов.
Однако отказ от использования функций отображения буферов и возврат к метафоре разделения и удаления окон также не является хорошей идеей. Функции отображения буферов предоставляют программам на Lisp и пользователям основу для согласования их различных потребностей; сравнимой основы для разделения и удаления окон не существует. Функции отображения буферов также позволяют по крайней мере частично восстановить компоновку фрейма при последующем удалении из него буфера (см. Завершение работы окон).
Ниже мы приведем ряд рекомендаций, чтобы искупить упомянутое выше разочарование и, таким образом, избежать буквальной потери буферов между окнами фрейма.
Написание действий отображения может быть мучительным, потому что приходится объединять функции действий и алисты действий в один огромный список. (Исторические причины помешали нам реализовать в display-buffer поддержку отдельных аргументов для них.) Возможно, поможет запоминание некоторых базовых форм, например перечисленных ниже:
'(nil (inhibit-same-window . t))
указывает только запись алиста действия и не указывает функцию действия. Единственная цель этого — запретить функции display-buffer-same-window, указанной в другом месте, показывать буфер в том же окне; см. также последний пример предыдущего подраздела.
'(display-buffer-below-selected)
с другой стороны, указывает одну функцию действия и пустой алист действия. Чтобы объединить эффекты двух приведенных выше спецификаций, нужно записать форму
'(display-buffer-below-selected (inhibit-same-window . t))
чтобы добавить еще одну функцию действия, нужно записать
'((display-buffer-below-selected display-buffer-at-bottom) (inhibit-same-window . t))
а чтобы добавить еще одну запись алиста, нужно записать
'((display-buffer-below-selected display-buffer-at-bottom) (inhibit-same-window . t) (window-height . fit-window-to-buffer))
Эта последняя форма может использоваться в качестве аргумента action для display-buffer следующим образом:
(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))))
Чтобы добавить настройку для второго буфера, нужно записать:
(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 должны быть готовы к тому, что пользовательские настройки могут привести к отображению буферов неожиданным образом. Они никогда не должны предполагать в своем последующем поведении, что буфер был показан именно так, как они просили в аргументе action функции display-buffer.
Пользователям не следует налагать слишком много и слишком строгих ограничений на то, как отображаются произвольные буферы. В противном случае они рискуют потерять особенности отображения буфера для определенной цели. Предположим, программа на Lisp была написана для сравнения различных версий буфера в двух окнах рядом. Если настройка display-buffer-alist предписывает, что любой такой буфер всегда должен показываться в выбранном окне или под ним, программе будет трудно настроить желаемую конфигурацию окон с помощью display-buffer.
Чтобы указать предпочтение для отображения произвольного буфера, пользователям следует настраивать display-buffer-base-action. Пример того, как пользователи, предпочитающие работать с несколькими фреймами, могут это сделать, был приведен в предыдущем подразделе. display-buffer-alist следует резервировать для отображения конкретных буферов определенным образом.
Как правило, для пользователей и программистов на Lisp всегда полезно быть готовыми к случаю, когда окно уже показывает данный буфер, и повторно использовать это окно. В предыдущем подразделе мы показали, что неправильное выполнение этого может привести к тому, что display-buffer будет постоянно создавать новый фрейм, хотя фрейм, показывающий этот буфер, уже существовал. Лишь в немногих случаях повторное использование окна может быть нежелательным, например, когда в этом окне должна отображаться другая часть буфера.
Следовательно, display-buffer-reuse-window — это одна из функций действия, которую следует использовать как можно чаще, как в аргументах action, так и в настройках. Запись inhibit-same-window в аргументе action обычно обрабатывает наиболее распространенный случай, когда повторное использование окна, показывающего буфер, следует избегать — случай, когда рассматриваемое окно является выбранным.
Это очевидно для людей, работающих с несколькими фреймами — фрейм, показывающий буфер, автоматически поднимется и получит фокус, если только запись inhibit-switch-frame не запрещает это. Для пользователей с одним фреймом эта задача может быть значительно сложнее. В частности, display-buffer-pop-up-window и display-buffer-use-some-window могут в этом отношении стать навязчивыми. Они разделяют или используют, казалось бы, произвольное (часто самое большое или наименее недавно использовавшееся) окно, отвлекая внимание пользователя.
Поэтому некоторые программы на Lisp пытаются выбрать окно в нижней части фрейма, например, чтобы отобразить буфер поблизости от окна минибуфера, где пользователь, как ожидается, ответит на вопрос, связанный с новым окном. Для действий, не связанных с вводом, display-buffer-below-selected может быть предпочтительнее, потому что выбранное окно обычно уже привлекает внимание пользователя.
Многие приложения вызывают display-buffer из обходов окон, создаваемых с помощью with-selected-window или вызовов select-window с аргументом norecord, не равным nil. Это почти всегда плохая идея, потому что окно, выбранное в рамках такого обхода, обычно не является окном, выбранным в конфигурации, представленной пользователю.
Если, например, пользователь добавил запись алиста inhibit-same-window, эта запись избежала бы окна, выбранного в пределах обхода, а не окна, выбранного в результирующей конфигурации. Даже если такая запись не была добавлена, результирующее поведение может быть странным. Находясь во фрейме, содержащем одно живое окно, вычисление следующей формы
(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 до завершения обхода.