Asynchronous 用异步调用重写函数值

Asynchronous 用异步调用重写函数值,asynchronous,emacs,elisp,lexical-scope,Asynchronous,Emacs,Elisp,Lexical Scope,如何在异步调用期间使用cl-letf或类似工具覆盖符号的函数值?我想在调用启动进程或启动进程shell命令后停止显示缓冲区,取而代之的是取回一个字符串 下面是一个简化的示例,其中绑定显示缓冲区适用于同步版本,但不适用于异步版本。此外,我还将词法绑定设置为true (defun tst-fun-sync (url) (call-process "wget" nil "*wget*" nil url "-O" "-") (with-current-buffer "*wget*" (d

如何在异步调用期间使用
cl-letf
或类似工具覆盖符号的函数值?我想在调用
启动进程
启动进程shell命令
后停止显示缓冲区,取而代之的是取回一个字符串

下面是一个简化的示例,其中绑定
显示缓冲区
适用于同步版本,但不适用于异步版本。此外,我还将词法绑定设置为true

(defun tst-fun-sync (url)
  (call-process "wget" nil "*wget*" nil url "-O" "-")
  (with-current-buffer "*wget*"
    (display-buffer (current-buffer))))

(defun tst-fun-async (url)
  (set-process-sentinel
   (start-process "wget" "*wget*" "wget" url "-O" "-")
   #'(lambda (p _m)
       (when (zerop (process-exit-status p))
         (with-current-buffer (process-buffer p)
           (display-buffer (current-buffer)))))))

(defun tst-fun-no-display (fun &rest args)
  (cl-letf (((symbol-function 'display-buffer)
             #'(lambda (&rest _ignored)
                 (message "%s" (buffer-string)))))
    (apply fun args)))

;; The first has desired result, but not the second
;; (tst-fun-no-display 'tst-fun-sync "http://www.stackoverflow.com")
;; (tst-fun-no-display 'tst-fun-async "http://www.stackoverflow.com")

让我们定义一个宏,它临时重新绑定
set process sentinel
,以便sentinel函数可以用包装器函数修饰

(defmacro with-sentinel-wrapper (wrapper-fn &rest body)
  (let ((sps (gensym))
        (proc (gensym))
        (fn (gensym)))
    `(let ((,sps (symbol-function 'set-process-sentinel)))
       (cl-letf (((symbol-function 'set-process-sentinel)
                  (lambda (,proc ,fn)
                    (funcall ,sps ,proc (funcall ,wrapper-fn ,fn)))))
                ,@body))))
包装器可以通过建立任何有用的动态绑定来更改调用sentinel的动态上下文。在这里,我重用您的
cl-letf
,以更改显示器的功能:

(with-sentinel-wrapper (lambda (fn)
                        (lexical-let ((fun fn)) 
                          (lambda (p m)
                            (cl-letf (((symbol-function 'display-buffer)
                                       #'(lambda (&rest _ignored)
                                           (message "%s" (buffer-string)))))
                              (funcall fun p m)))))
  (tst-fun-async "http://www.stackoverflow.com"))

现在,如果您不确定异步进程是否真的调用了
set process sentinel
,那么您可能需要破解其他函数。

使用
shell命令来字符串如何?例如,
(将regexp替换为字符串“\n”“”(shell命令到字符串“date”))
不需要显示缓冲区来使用它——也就是说,在我看到的示例中,不需要使用
显示缓冲区
。您可能还对可以以字符串形式捕获所有流程输出的流程过滤器感兴趣。谢谢,但这似乎与我的尝试结果相同——对异步版本没有影响。我认为它不会工作,因为在异步调用之前,unwind protect会对bodyforms进行保护finishes@jenesaisquoi谢谢,我现在明白了。我改变了答案。不是很完美,但很管用。太棒了,谢谢!所以我猜词法环境在一个常规的进程sentinel调用中被抛出了?我希望我能更好地理解这一点。我最初尝试过入侵哨兵,但没能成功。幸运的是,Im目标函数确实使用了set process sentinel,因此perfect@jenesaisquoi谢谢
cl-letf
,除非另有规定,否则emacs中的大多数绑定都是动态的:它们像调用堆栈一样来来去去。因此,当您退出调用进程创建的函数时,任何有效的绑定都将消失。稍后,当调用sentinel时,代码将与全局绑定一起运行。我在这里所做的是确保在调用sentinel时,我们的定制sentinel
(lambda(p m)…
更改调用实际sentinel
fun
的动态上下文(请注意,我在这里使用let词法存储实际sentinel函数以备将来使用),我知道elisp默认为动态范围,并且正在使用
;-*-词法绑定:t-*-
位于脚本顶部,因此使用
let
代替
lexicallet
也可以。我原以为闭包会把sentinel包起来——我想知道使用
processput
将函数附加到进程是否是另一种选择。再次感谢,非常有帮助:)