使用make-process, :sentinel 中 ( funccall lambda ) 报错:variable is void

起因:想抄 citre/citre-ctags.el at master · universal-ctags/citre · GitHub , 先生成一个临时的 tag 文件,处理好后再把 citre 默认用的 tags 文件替换为临时文件。当运行到 :sentinel 时,会报下面的错误,和 citre 的 citre-update-updatable-tags-file 比较了好久,始终没找出差别,烦请各位赐教

error in process sentinel: let: Symbol’s value as variable is void: after-process
error in process sentinel: Symbol’s value as variable is void: after-process

报错代码如下:

(defun citre-auto-update-updatable-tags-file (&optional tagsfile sync)
  " copy from `citre-upate-updateale-tas-file'

Update TAGSFILE that contains recipe for updating itself.
If the recipe can't be found, throw an error.

When SYNC is non-nil, update TAGSFILE synchronously.

Return t if the ctags process starts successfully (when updating
asynchronously), or the updating is finished (when updating
synchronously).  Otherwise return nil."
  (when-let* ((tagsfile
               (or tagsfile (citre-read-tags-file-name)))
              (cmd-ptag (citre--get-pseudo-tag-value "CITRE_CMD" tagsfile))
              (cmd (citre--ctags-cmd-ptag-to-exec cmd-ptag tagsfile))
              (cwd-ptag (citre--get-pseudo-tag-value "TAG_PROC_CWD" tagsfile))
              (cwd (if-let ((remote-id (file-remote-p tagsfile)))
                       (concat remote-id cwd-ptag) cwd-ptag))
              (proc-name (concat tagsfile "." "proc"))
              (after-process (lambda ()
                               (citre--write-ctags-recipe
                                tagsfile cmd-ptag cwd-ptag)
                               (sit-for 1)
                               (rename-file tagsfile (string-replace ".auto-update" "" tagsfile ) t)
                               (citre-clear-tags-file-cache)
                               (remhash (funcall citre-project-root-function) citre-auto-update-process-table)
                           ))
              )
    ;; Workaround: If we put this let into the above `if-let*' spec, even
    ;; if it stops before let-binding `default-directory', later there'll
    ;; be some timer errors.
    (let ((default-directory cwd))
      ;; (puthash (funcall citre-project-root-function) proc-name citre-auto-update-process-table)
      (if sync
          (progn (apply #'process-file (car cmd) nil
                        (get-buffer-create "*citre-ctags*") nil (cdr cmd))
                 (funcall after-process))
        (make-process
         :name proc-name
         :buffer (get-buffer-create "*citre-ctags*")
         :command cmd
         :connection-type 'pipe
         :stderr nil
         :sentinel
         (lambda (proc _msg)
           (pcase (process-status proc)
             ('exit
              (pcase (process-exit-status proc)
                (0 (funcall after-process)
                   (message "Finished updating %s" tagsfile))
                (s (user-error "Ctags exits %s.  See *citre-ctags* buffer"
                               s))))
             (s (user-error "Abnormal status of ctags: %s.  \
See *citre-ctags* buffer" s))))
         :file-handler t)
        (message "Auto-updating %s..." tagsfile))
      t)))

after-process 不要放在 (when-let* ((after-process))), 使用

(when-let* (()) (setq after-process))。

谢谢大佬,这看起来是生成了一个全局的的 lambda,类似 func, lambda 里面用的变量需要全部重新生成,很奇怪 citre 为什么这么干就没有问题。

还想请教一下大佬,在运行 after-process 的时候,default-directory 是执行 make-process 时的值,还是会随当前激活的 buffer 变化?比如我执行 make-process 时在 dirA/buffer-A,这个sub-process要执行很长时间才结束,结束的时候buffer已经切换到 dirb/buffer-B

你问的其他问题,我不懂啊,我不是程序员。

只是你这个问题我最近碰到了。

大佬是论坛里的几位管理员,我帮你不到什么 :grinning:

when-let* 是前面的表达式都要是 non-nil 才会执行下面的赋值操作, 你要看看 after-process 之前的那些变量是否是 non-nil, 如果有一个是 nil, after-process 变量本身就可能并没有赋值, 也不会存在。

你可以尝试把 when-let* 直接替换成 let*, 然后在下面 body 里需要条件判断的时候再加 if 或 when 语句。

只是看代码,并没有测试代码, 我的回答不一定正确。

可能是 lexical-binding 设置的问题,导致第二句 (funcall after-process) 的时候 after-process 失效。

这里我测试过,在 make-process 之前可以拿到正常的 after-process

哇噻,真的,就是这个,加上就好了 :100:

;;;  -*- lexical-binding: t -*-