首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
297 阅读
2
从零开始配置 vim(15)——状态栏配置
174 阅读
3
nvim番外之将配置的插件管理器更新为lazy
163 阅读
4
2018总结与2019规划
152 阅读
5
wsl 启动报错:Wsl/0x80080005
128 阅读
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
登录
Search
标签搜索
c++
c
emacs
elisp
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
win32
读书笔记
VimScript
linux
文本编辑器
投资理财
Java
反汇编
OLEDB
数据库编程
Masimaro
累计撰写
462
篇文章
累计收到
33
条评论
首页
栏目
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
页面
归档
友情链接
关于
搜索到
1
篇与
的结果
2026-10-24
《elisp 每日一练》——异步流式传输与 Process Filter
在之前一篇文章中,我们了解了通过Emacs异步调用进程,防止耗时进程卡住主界面。但是细心的读者会发现之前介绍的方法有一个很大的问题:我们的输出都是从buffer中获取的,创建buffer会额外消耗资源,另外如果要利用外部进程输出的内容做一些事情,我们需要固定的从buffer中读取。但是我们怎么知道buffer中有内容或者和上次相比有新内容出现,这就需要我们自己主动的不断去访问buffer。或者等待进程结束之后一次性读取所有内容。这样似乎又变成了需要等待的同步事件。对于这种场景,最理想的方式就是注册一个异步处理方式,只有在有新内容的时候触发。要实现这个需求,Emacs 为我们提供了 Process Filter 机制背景知识所谓的 Process Filter 是 Emacs 异步进程架构中的信息拦截器。它的工作原理是,Emacs调用进程时,进程的标准输出会被操作系统管道推送到Emacs端。Emacs 捕获到这些增量数据后,会立刻中断并调用你注册的 Filter 函数,将刚吐出的文本片段(String 增量)传递给你。如果不指定,那么会调用默认的 Filter 函数,该函数只是机械的将新内容写回到指定的buffer中。一个标准的 Filter 回调函数的签名通常如下:(defun my-custom-filter (proc output-string) )其中 proc 是当前的进程对象。而output-string 则是当前增量更新的程序输出。这里请注意, output-string 并不保证数据是完整一行或者是完整的结构,它只是机械的将本次输出的内容传进来。有时候数据会出现残缺,所以我们在处理的时候需要关注数据的完整性,有时候需要自己处理恢复完整数据,这算是另一种形式的“粘包”filter 函数的注册可以利用我们在前面介绍 make-process 的时候提到的 :filter 关键字练习我们对昨天的程序做一个简单的变更。cmake 编译会输出大量信息,一般来讲我们只关心当前编译的进度以及错误出现时的报错信息。如果cmake 大量输出,Emacs需要频繁往buffer中写入新内容并刷新显示可能会造成卡顿。因此我们利用filter实现按需分批次写入缓冲区的功能。(defvar my-build-progress nil "当前构建进度百分比,nil 表示未在构建。") (defun my-build-progress-filter (proc output) "节流并解析 OUTPUT 中的进度,同时把完整输出写入进程缓冲区。" (let ((buf (process-buffer proc))) (when (buffer-live-p buf) (with-current-buffer buf (goto-char (point-max)) (insert output)))) (let ((start 0) (last-pct nil)) (while (string-match "\\([0-9]+\\)%" output start) (setq last-pct (string-to-number (match-string 1 output))) (setq start (match-end 0))) (when (and last-pct (not (equal last-pct my-build-progress))) ;; 只有进度更新时才打印信息 (setq my-build-progress last-pct) (message "构建进度: %d%%" last-pct)))) (defun my-cmake-msvc-build (source-dir) "在 SOURCE-DIR 下执行 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)) ;; 错误缓冲区启用 compilation-minor-mode,支持跳转 (with-current-buffer err-buf (erase-buffer) (compilation-minor-mode t)) ;; 构建输出缓冲区也启用编译模式,兼容走 stdout 的链接错误 (with-current-buffer out-buf (erase-buffer) (compilation-minor-mode t)) (setq my-build-progress nil) (force-mode-line-update) (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 :filter #'my-build-progress-filter :noquery t :sentinel (lambda (p1 event1) (when (memq (process-status p1) '(exit signal)) (setq my-build-progress nil) (if (zerop (process-exit-status p1)) (message "构建成功") (message "构建失败,查看 *compile-error*") (display-buffer (if (> (with-current-buffer err-buf (buffer-size)) 0) err-buf out-buf)))))))) (message "CMake 配置失败,查看 *compile-error*") (display-buffer err-buf))))) config-proc))本次我们添加了一个名为 my-build-progress-filter 的过滤函数,它主要的作用是将新增的内容插入buffer、并且提取里面进度信息。虽然这里频繁的插入信息到buffer中,但是如果当前Emacs不显示buffer的话,它并不会立即重绘并刷新buffer,只有在调用 message 或者 force-mode-line-update 这种显式刷新的函数才会刷新当前buffer显示。这里只有在进度变化时才刷新,算是降低了刷新频率。后面编译进程相关的代码就与前面的类似。只是通过 :filter 指定了函数总结本篇我们讨论了 make-process 中的 :filter 用法,绑定一个函数作为回调,每当进程有新的输出时被调用,该函数可以进行数据的解读或者控制数据插入buffer中的行为。函数需要接受两个参数:proc 是对应的进程对象、output 是本次调用新增的字符串
2026年10月24日
1 阅读
0 评论
0 点赞