Как поймать все вызовы функций ELISP, которые требуют взаимодействия пользователя?

StackOverflow https://stackoverflow.com/questions/3401969

Вопрос

Я хочу написать функцию, которая позвонит список функций в Emacs (в частности, kill-buffer-query-functions), но если кто-то из них требует взаимодействия пользователя, я хочу, чтобы они просто вернулись nil Вместо этого, так что все это будет работать не интерактивно. Я думаю об использовании defadvice Чтобы изменить каждую функцию, которая обычно предлагает пользователю вместо throw исключение со значением nil. Отказ Тогда я обрушу все catch форма. Единственная проблема в том, что у меня нет исчерпывающего списка всех функций EMACS ELISP, которые могут запросить пользователю что-то.

У кого-нибудь есть такой список, или есть ли проще поступить об этом? В качестве примеров функции yes-or-no-p а также y-or-n-p наверняка будет в этом списке, как бы read-string а также completing-read.

По сути, я хочу заполнить эллипсис в этом коде:

(defun eval-but-return-if-requires-user (form &optional interactive-return)
  "Evaluate FORM, but immediately stop and return INTERACTIVE-RETURN if any part of FORM would require user interaction."
  ;; Set up the safety net
  (catch 'ui-required 
    (let ((ui-requiring-functions '(  ...  )) ; What goes in this list?
          (ui-required-callback 
           ;; A function that takes any number of args and throws an exception
           '(lambda (&rest args) 
              (throw 'ui-required interactive-return)))
          (flet-redefinitions
           ;; Use the above callback to create a list of redefinitions for `flet'
           (mapcar (lambda (func) 
                     (cons func (cdr ui-required-callback))) 
                   ui-requiring-functions)))
      `(flet 
           ,flet-redefinitions          ; Perform the function redefinitions,
         ,form))))                      ; Then evaluate FORM

По сути, я оборужу все в блоке Catch, то я определяю функциональный корпус, который просто будет принимать любые аргументы и бросать соответствующее исключение. Я настроил список переопределений, которые будут переданы flet, который временно переопределит те функции для использования вышеупомянутого корпуса, бросающего исключение. Наконец, я оцениваю form с этими временными функциями переопределения на месте.

Теперь, вероятно, некоторые какие-то цитируют ошибки в этом коде, но я думаю, что это будет работать, если бы у меня только был соответствующий список функций для переопределения.

Обратите внимание, что я хочу, чтобы вся форма вернулась, если требуется какое-либо взаимодействие пользователей, а не только определенный вызов функции в форме, требующую взаимодействия пользователя. Чтобы понять, почему мне это нужно, считайте, что форма может захотеть задать любой из следующих вопросов:

  • Вы хотите удалить этот очень важный файл? да или нет
  • Вы хотите сохранить этот очень важный файл? да или нет

Очевидно, если я просто модифицировал yes-or-no-p всегда возвращать nil (что означает «нет»), что до сих пор не гарантируется сохранение моего важного файла от удаления. Итак, поскольку я не знаю, какие вопросы могут спросить, я хочу вызвать всю форму просто прервать и вернуть конкретное значение, если он хочет спросить что-либо о пользователе.

По сути, я хочу сказать «оценить этот код, но если вы должны задать мне что-нибудь, чтобы сделать это, просто забудь об этом и вернуть nil."

Это было полезно?

Решение 2

Я просто наткнулся на возможный ответ на это:

M-x describe-function minibuffer-with-setup-hook

(Minibuffer-с настроенным корпусом для удовольствия и отдыха)

Добавьте удовольствие к `Minibuffer-setup-took 'при выполнении тела. Тело должно использовать минибуфер максимум один раз. Рекурсивное использование минибуфера не будет затронута.

Итак, используя это, если я хотел оценить BODY но прервать и вернуться nil если BODY пытается использовать минибуфер, может быть, я мог бы использовать это:

(catch 'tried-to-use-minibuffer
  (minibuffer-with-setup-hook 
      (lambda (&rest args) (throw 'tried-to-use-minibuffer nil))
    BODY))

Я проверю это позже.


Это позже, и я проверил это. Работает отлично. Вот мой последний код для этого:

(defmacro without-minibuffer (&rest body)
  "Like `progn', but stop and return nil if any of BODY forms tries to use the minibuffer.

Also disable dialogs while evaluating BODY forms, since dialogs
are just an alternative to the minibuffer."
  `(catch 'tried-to-use-minibuffer
     (minibuffer-with-setup-hook
         (lambda (&rest args) (throw 'tried-to-use-minibuffer nil))
       (let ((use-dialog-box))          ; No cheating by using dialogs instead of minibuffer
         ,@body))))

;; This should be indented like progn
(put 'without-minibuffer 'lisp-indent-function (get 'progn 'lisp-indent-function))

Другие советы

Я думаю, что вы можете просто использовать «CommandP», чтобы сказать, является ли конкретная функция интерактивной.

Так просто бегите (remove-if 'commandp kill-buffer-query-functions)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top