interactiveВ этом разделе описывается, как писать форму interactive, которая
делает функцию Lisp интерактивно вызываемой командой, а также как
исследовать форму interactive команды.
Эта специальная форма объявляет, что функция является командой и, следовательно, может вызываться интерактивно (через M-x или вводом последовательности клавиш, привязанной к ней). Аргумент arg-descriptor определяет, как вычислять аргументы команды при её интерактивном вызове.
Команда может вызываться из программ на Lisp, как и любая другая функция, но в этом случае вызывающий передаёт аргументы, и arg-descriptor не оказывает влияния.
Форма interactive должна находиться на верхнем уровне в теле
функции или в свойстве interactive-form символа функции
(см. Свойства символов). Она оказывает своё действие, потому что
командный цикл ищет её перед вызовом функции
(см. Интерактивный вызов). После вызова функции выполняются все формы
её тела; в этот момент, если форма interactive встречается внутри
тела, она просто возвращает nil, даже не вычисляя свой аргумент.
Список modes позволяет указать, в каких режимах предполагается использовать команду. Подробнее о действии указания modes и о том, когда его использовать, см. Указание режимов для команд.
Согласно соглашению, следует помещать форму interactive в тело
функции первой формой на верхнем уровне. Если форма interactive
присутствует и в свойстве символа interactive-form, и в теле
функции, приоритет имеет первая. Свойство символа
interactive-form можно использовать для добавления интерактивной
формы к существующей функции или изменения способа интерактивной обработки
её аргументов без переопределения функции.
Для аргумента arg-descriptor возможны три варианта:
nil; тогда команда вызывается без
аргументов. Это быстро приводит к ошибке, если команда требует один или
несколько аргументов.
interactive), за которым может следовать приглашение (которое
некоторые кодовые символы используют, а некоторые игнорируют). Вот пример:
(interactive "P\nbFrobnicate buffer: ")
Кодовая буква ‘P’ устанавливает первый аргумент команды в значение сырого префикса команды (см. Аргументы префиксных команд). ‘bFrobnicate buffer: ’ запрашивает у пользователя с приглашением ‘Frobnicate buffer: ’ ввод имени существующего буфера, который становится вторым и последним аргументом.
Строка приглашения может использовать ‘%’ для включения предыдущих
значений аргументов (начиная с первого) в приглашение. Это делается с
помощью format-message (см. Форматирование строк). Например, вот как
можно прочитать имя существующего буфера, а затем новое имя для этого
буфера:
(interactive "bBuffer to rename: \nsRename buffer %s to: ")
Если ‘*’ стоит в начале строки, то сигнализируется ошибка, если буфер только для чтения.
Если ‘@’ стоит в начале строки и последовательность клавиш, использованная для вызова команды, содержит события мыши, то окно, связанное с первым из этих событий, выбирается перед выполнением команды.
Если ‘^’ стоит в начале строки и команда была вызвана через
сдвиговую трансляцию, то перед выполнением команды временно
устанавливается метка и активируется регион, либо расширяется уже активный
регион. Если команда была вызвана без сдвиговой трансляции и регион временно
активен, то перед выполнением команды регион деактивируется. Сдвиговая
трансляция управляется на пользовательском уровне с помощью
shift-select-mode; см. Выбор сдвигом в Руководстве GNU
Emacs.
Можно использовать ‘*’, ‘@’ и ^ вместе; порядок не
имеет значения. Фактическое чтение аргументов управляется остальной частью
строки приглашения (начиная с первого символа, который не является
‘*’, ‘@’ или ‘^’).
Передача точки или метки в качестве значения аргумента также распространена, но если вы делаете это и читаете ввод (будь то с использованием минибуфера или нет), обязательно получайте целочисленные значения точки или метки после чтения. Текущий буфер может получать вывод подпроцесса; если вывод подпроцесса поступает, пока команда ожидает ввод, это может переместить точку и метку.
Вот пример того, что не следует делать:
(interactive
(list (region-beginning) (region-end)
(read-string "Foo: " nil 'my-history)))
Вот как избежать проблемы, проверяя точку и метку после чтения ввода с клавиатуры:
(interactive (let ((string (read-string "Foo: " nil 'my-history))) (list (region-beginning) (region-end) string)))
Предупреждение: значения аргументов не должны включать типы
данных, которые нельзя напечатать и затем прочитать. Некоторые средства
сохраняют command-history в файл для чтения в последующих сеансах;
если аргументы команды содержат тип данных, который печатается с
использованием синтаксиса ‘#<…>’, эти средства не будут работать.
Однако есть несколько исключений: допустимо использовать ограниченный набор
выражений, таких как (point), (mark),
(region-beginning) и (region-end), потому что Emacs
распознаёт их особым образом и помещает в историю команд выражение (а не его
значение). Чтобы узнать, является ли написанное вами выражение одним из этих
исключений, выполните команду, а затем проверьте
(car command-history).
Эта функция возвращает форму interactive функции function.
Если function является интерактивно вызываемой функцией
(см. Интерактивный вызов), возвращаемое значение — это форма
interactive команды (interactive spec), которая
определяет, как вычислять её аргументы. В противном случае значение равно
nil. Если function является символом, используется его
определение функции.
При вызове на OClosure работа делегируется обобщённой функции
oclosure-interactive-form.
Как и interactive-form, эта функция принимает команду и возвращает
её интерактивную форму. Отличие в том, что это обобщённая функция, и она
вызывается только когда function является OClosure
(см. Открытые замыкания). Цель — дать возможность некоторым типам
OClosure динамически вычислять свои интерактивные формы вместо хранения их в
одном из своих слотов.
Это используется, например, для функций kmacro, чтобы уменьшить их
размер в памяти, поскольку все они используют одну и ту же интерактивную
форму. Это также используется для функций advice, где интерактивная
форма вычисляется на основе интерактивных форм её компонентов, чтобы
выполнять это вычисление более лениво и правильно корректировать
интерактивную форму при переопределении одного из её компонентов.