Порядок применения функций действий
Из предыдущих разделов мы уже знаем, что display-buffer необходимо предоставить ряд действий отображения (см. Выбор окна), чтобы отобразить буфер. В полностью не настроенном Emacs эти действия определяются display-buffer-fallback-action в следующем порядке приоритета: повторное использование окна, открытие нового окна в той же вкладке, использование окна, в котором ранее отображался буфер, использование произвольного окна и открытие новой вкладки. (Обратите внимание, что оставшиеся действия, перечисленные display-buffer-fallback-action, недействительны в не настроенном Emacs).
Рассмотрим следующий формат:
(display-buffer (get-buffer-create "*foo*"))
Выполнение этой формы в буфере *scratch* сессии не настроенного Emacs обычно не позволит повторно использовать окно, в котором уже отображается *foo*, но позволит открыть новое окно. Повторное выполнение той же формы не вызовет видимых изменений — display-buffer повторно использовало окно, в котором уже отображался *foo*, поскольку это действие было применимо и имело наивысший приоритет среди всех применимых действий.
Открытие нового окна не удастся, если на выбранной вкладке недостаточно места. В не настроенном Emacs это обычно происходит, если на вкладке уже есть два окна. Например, если вы сейчас наберете C-x 1, а затем C-x 2 и выполните форму еще раз, *foo* должно отобразиться в нижнем окне — display-buffer просто использовало «произвольное» окно. Если до ввода C-x 2 вы ввели C-x o, *foo* отобразилось бы в верхнем окне, так как «произвольное» окно означает «наименее недавно используемое» окно, а выбранное окно наименее недавно использовалось, если и только если оно единственное на своей вкладке.
Предположим, что вы не вводили C-x o, и *foo* отображается в нижнем окне. Наберите C-x o, затем C-x left и выполните форму ещё раз. Это должно отобразить *foo* в том же нижнем окне, потому что это окно уже ранее отображало *foo* и, следовательно, было выбрано вместо другого окна.
До сих пор мы наблюдали только поведение по умолчанию в сессии не настроенного Emacs. Чтобы увидеть, как это поведение можно настроить, рассмотрим параметр display-buffer-base-action. Он предоставляет очень грубую настройку, которая концептуально влияет на отображение любого буфера. Он может быть использован для дополнения действий, предоставленных display-buffer-fallback-action, переупорядочивая их или добавляя действия, отсутствующие в них, но более точно соответствующие практике редактирования пользователя. Однако он также может быть использован для изменения поведения по умолчанию более радикальным способом.
Рассмотрим пользователя, который, как правило, предпочитает отображать буферы на другой вкладке. Такой пользователь может выполнить следующую настройку:
(customize-set-variable 'display-buffer-base-action '((display-buffer-reuse-window display-buffer-pop-up-frame) (reusable-frames . 0)))
Эта настройка заставит display-buffer сначала попытаться найти окно, отображающее буфер на видимой или свернутой вкладке, и, если такая вкладка не существует, открыть новую вкладку. Вы можете наблюдать это поведение в графической системе, набрав C-x 1 в окне отображения *scratch* и выполнив нашу каноническую 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-reuse-window кажется излишним в настройке display-buffer-base-action, так как уже является частью display-buffer-fallback-action и должно быть опробовано там в любом случае. Однако это не сработает, поскольку из-за приоритета display-buffer-base-action над display-buffer-fallback-action, в то время display-buffer-pop-up-frame уже выиграло гонку. На самом деле это:
(customize-set-variable 'display-buffer-base-action '(display-buffer-pop-up-frame (reusable-frames . 0)))
приведет к тому, что display-buffer всегда будет открывать новую вкладку, что, вероятно, не то, чего хочет наш пользователь.
До сих пор мы показали, как пользователи могут настроить поведение по умолчанию display-buffer. Давайте теперь посмотрим, как приложения могут изменить ход display-buffer. Канонический способ сделать это — использовать аргумент action display-buffer или функцию, которая его вызывает, например, pop-to-buffer (см. Переключение буферов).
Предположим, что приложение хочет отобразить *foo* предпочтительно ниже выбранного окна (чтобы немедленно привлечь внимание пользователя к новому окну) или, если это не удастся, в окне внизу вкладки. Это можно сделать с помощью такого вызова:
(display-buffer (get-buffer-create "*foo*") '((display-buffer-below-selected display-buffer-at-bottom)))
Чтобы увидеть, как работает эта новая, изменённая форма, удалите любые вкладки, отображающие *foo*, введите C-x 1, затем C-x 2 в окне отображения *scratch* и затем выполните эту форму. display-buffer должно разделить верхнее окно и показать *foo* в новом окне. В противном случае, если после C-x 2 вы ввели C-x o, display-buffer бы разделило окно внизу.
Предположим теперь, что перед выполнением новой формы вы сделали выбранное окно как можно меньше, например, выполнив форму (fit-window-to-buffer) в этом окне. В этом случае, display-buffer не смог бы разделить выбранное окно и разделило бы корневое окно вкладки вместо этого, эффективно отобразив *foo* внизу вкладки.
В любом случае, повторное выполнение новой формы должно повторно использовать окно, в котором уже отображается *foo*, так как обе функции, предоставленные аргументом action, сначала пытаются повторно использовать такое окно.
Установив аргумент action, приложение фактически переопределяет любую настройку display-buffer-base-action. Теперь пользователь может либо принять выбор приложения, либо дополнительно настроить параметр display-buffer-alist следующим образом:
(customize-set-variable
'display-buffer-alist
'(("\\*foo\\*"
(display-buffer-reuse-window display-buffer-pop-up-frame))))
Попытка сделать это с новой, изменённой формой выше в конфигурации, где *foo* не отображается, покажет *foo* на отдельной вкладке, полностью игнорируя аргумент action display-buffer.
Обратите внимание, что мы не удосужились указать запись списка действий reusable-frames в нашем определении display-buffer-alist. display-buffer всегда берет первую найденную запись — в нашем случае ту, что указана display-buffer-base-action. Если мы захотели использовать другое определение, например, исключить свернутые вкладки, отображающие *foo*, из списка повторно используемых, нам пришлось бы указать это отдельно, например:
(customize-set-variable
'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 по двум основным аспектам: она сильнее, потому что переопределяет аргумент action display-buffer, и она позволяет явно указать затронутые буферы. Фактически, отображение других буферов никак не затрагивается настройкой для *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))))
Выполнение этой формы обычно отобразит *foo* в выбранном окне независимо от аргумента action и любых пользовательских настроек. (Обычно приложения не заморачиваются, чтобы также предоставить аргумент 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 выше, переопределив определение, предоставленное display-buffer-base-action. Предположим, что наш пользователь написал это как
(customize-set-variable
'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*")))
Этот последний пример показывает, что, хотя порядок приоритета функций действий фиксирован, как описано в Выбор окна, запись списка действий, заданная действием отображения, ранжированным ниже в этом порядке, может влиять на выполнение действия отображения с более высоким рангом.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Precedence-of-Action-Functions.html