使用异步调用覆盖函数值

时间:2016-12-06 05:13:46

标签: asynchronous emacs elisp lexical-scope

如何在异步调用期间使用cl-letf或类似内容覆盖符号的函数值?我希望在调用start-processstart-process-shell-command后停止显示缓冲区,而是取回字符串。

这是一个简化示例,其中绑定display-buffer适用于同步版本但不适用于异步版本。另外,我将lexical-binding设置为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")

1 个答案:

答案 0 :(得分:2)

让我们定义一个临时重新绑定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))))

包装器可以通过建立任何有用的动态绑定来更改调用标记的动态上下文。在这里,我重复使用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,则可能需要破解其他函数。