在上一节,我们讨论了Emacs中同步方式调用进程,它比较简单,适合那些能快速返回的命令行程序,对于一些耗时操作,如果下一步确实依赖耗时操作的返回,那么等待可能是必须的。但是某些时候,如果因为一些功能增强的后台应用导致界面卡顿,例如拼写检查、lsp 代码分析 如果耗时太久反而影响用户编写代码的体验。对于这种我们可以考虑将它们放到后台作为异步进程
背景知识
Emacs 提供了两个启动异步进程的函数: start-process 和 make-process。前者是早期Emacs提供的方式,它比较简单和传统,函数签名如下:
(start-process NAME BUFFER PROGRAM &rest PROGRAM-ARGS)它的功能比较简单,它启动一个异步进程并将进程输出放到指定的buffer中,这种情况下我们无法对进程生命周期和输出进行精准的控制,如果需要更精确的控制,更推荐使用 make-process
(make-process &rest ARGS)它的签名看着简单,似乎只有一个 ARGS 作为可选,但是实际上可选的参数十分丰富。我们传入一些关键字参数来精确的控制异步进程。最关键的几个参数如下:
:name进程名称,Emacs必要时会自动修改这个名称,确保唯一;请注意这个名称是我们为这个进程对象取得名字,并不是可执行程序得名称:command这个是进程真实执行得命令行,可以是一个个命令行参数组成得字符串列表:buffer指定进程输出关联的缓冲区,可以是缓冲区名称或者对象:filter输出过滤函数,如果指定了函数,有输出时会调用该函数进行过滤,并将过滤后的内容输出到指定buffer中:sentinel进程状态变更的回调函数:stderr标准错误输出的buffer
调用这些函数之后,会返回一个进程对象。我们后面可以以对象作为参数,调用下面的函数来控制它的生命周期:
process-live-p: 检查进程当前是否还在活跃运行process-status: 获取进程状态,例如'run、'exit、'signalinterrupt-process: 向进程发送中断信号kill-process: 强行终止进程
练习
我们实现一个调用cmake编译的小插件。假设我有一个项目在Windows上,我执行编译的命令如下:
cmake . -B build -G"Visual Studio 18 2026" -T "v143" -A x64 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL
cmake --build build将编译的正常输出放到名为 *compile-build* 的buffer中,将错误和警告信息放到 *compile-error* 我们可以在该buffer中开启 compilation-minor-mode,这样我们就可以根据行号快速打开报错的位置
(defun my-cmake-msvc-build (source-dir)
"打开当前buffer所在目录,执行cmake编译"
(interactive "D选择项目根目录:")
(let* ((default-directory (file-name-as-directory source-dir))
(build-dir "build")
(out-buf (get-buffer-create "*compile-build*"))
(err-buf (get-buffer-create "*compile-error*"))
(config-proc nil) ;; 构建进程
(build-proc nil)) ;; 编译进程
(with-current-buffer err-buf ;;启用 compilation-minor-mode 支持从错误信息跳转到具体位置
(erase-buffer)
(compilation-minor-mode t))
(with-current-buffer out-buf
(erase-buffer))
(message "开始cmake 配置")
(setq config-proc
(make-process
:name "cmake-config"
:command (list "cmake" "." "-B" build-dir
"-G" "Visual Studio 18 2026"
"-T" "v143"
"-A" "x64"
"-DCMAKE_BUILD_TYPE=Debug"
"-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL")
:buffer out-buf
:stderr err-buf
:noquery t
:sentinel
;; 在构建成功后执行编译
(lambda (p event)
(when (memq (process-status p) '(exit signal))
(if (zerop (process-exit-status p))
(progn
(message "构建成功,开始编译...")
(setq build-proc
(make-process
:name "cmake-build"
:command (list "cmake" "--build" build-dir)
:buffer out-buf
:stderr err-buf
:noquery t
:sentinel
;; 通知结果
(lambda (p1 event1)
(when (memq (process-status p1) '(exit signal))
(if (zerop (process-exit-status p1))
(message "构建成功")
(message "构建失败,查看*compile-error*")
(display-buffer err-buf))))))))))))
config-proc))这个例子,我们演示了 make-process 的基本用法,并且给出了状态变更的回调函数的使用。在Windows上实测时因为msvc将编译时的错误信息和正常输出都作为标准输出了,所以读者可能会发现报错出现在 *compile-build* 缓冲。为了适配Windows,读者可以尝试在 *compile-build* 也加上 compilation-minor-mode
总结
本节我们介绍了异步方式调用的进程的方法,本节的相关函数如下:
make-processprocess-live-pprocess-statusinterrupt-processkill-process
评论 (0)