Как перехватить все вызовы функций elisp, которые требуют взаимодействия с пользователем? - PullRequest
1 голос
/ 04 августа 2010

Я хочу написать функцию, которая будет вызывать список функций в 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 с учетом этих временных переопределений функций.

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

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

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

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

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

Ответы [ 2 ]

0 голосов
/ 05 августа 2010

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

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

(минибуфер с установочным крючком FUN & rest BODY)

AddПрикоснитесь к 'minibuffer-setup-hook' во время выполнения BODY.ТЕЛО должно использовать минибуфер максимум один раз.На рекурсивное использование минибуфера это не повлияет.

Итак, используя это, если я хочу оценить 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))
0 голосов
/ 04 августа 2010

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

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

...