如何捕获需要用户交互的所有elisp函数调用?

时间:2010-08-04 01:33:34

标签: try-catch elisp non-interactive

我想编写一个函数来调用emacs中的函数列表(具体来说,kill-buffer-query-functions),但如果其中任何一个函数需要用户交互,我想让它们只返回nil代替,以便整个事情将以非交互方式运行。我正在考虑使用defadvice来修改通常会提示用户throw一个值为nil的异常的每个函数。然后我将用catch形式包装所有内容。唯一的麻烦是,我没有详尽列出所有可能提示用户输入内容的emacs elisp函数。

有没有人有这样的清单,还是有更简单的方法来解决这个问题?例如,函数yes-or-no-py-or-n-p肯定会出现在此列表中,read-stringcompleting-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 个答案:

答案 0 :(得分:0)

我认为您可以使用“commandp”来判断特定功能是否是交互式的。

所以只需运行(remove-if 'commandp kill-buffer-query-functions)

答案 1 :(得分:0)

我偶然发现了一个可能的答案:

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

  

(迷你缓冲器 - 安装 - 挂钩FUN&休息   BODY)

     

添加FUN到'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))