当某个操作需要较长时间才能完成时,应当向用户告知其执行进度。这样用户可以预估剩余时间,并明确知晓 Emacs 正在繁忙工作而非卡死。实现该功能的简便方式是使用 进度报告器(progress reporter)。
以下是一个无实际功能但可正常运行的示例:
(let ((progress-reporter
(make-progress-reporter "Collecting mana for Emacs..."
0 500)))
(dotimes (k 500)
(sit-for 0.01)
(progress-reporter-update progress-reporter k))
(progress-reporter-done progress-reporter))
该函数创建并返回一个进度报告器对象,该对象将作为下文所列其他函数的参数。其设计思路是预先计算尽可能多的数据,以保证进度报告的高效执行。
后续使用该进度报告器时,它会在回显区显示 message,并在其后附加进度百分比。message 按普通字符串处理。例如若需要其依赖文件名,应在调用该函数前使用 format-message 处理。
参数 min-value 和 max-value 应为数字,分别代表操作的起始与结束状态。例如扫描缓冲区的操作可将其分别设为 point-min 和 point-max 的返回值。max-value 必须大于 min-value。
你也可以将 min-value 和 max-value 设为 nil。此时进度报告器不会显示百分比,而是显示一个 “旋转指示器(spinner)”,每次更新进度时转动一格。
若 min-value 和 max-value 为数字,可通过参数 current-value 指定初始进度数值;若省略则默认为 min-value。
其余参数控制回显区的更新频率。进度报告器会等待操作至少完成 min-change 百分比后才打印下一条信息,默认值为百分之一。min-time 指定连续打印之间的最小间隔秒数,默认值为 0.2 秒。(部分操作系统对秒级小数的处理精度可能不同。)
该函数会调用 progress-reporter-update,因此第一条信息会立即打印。
该函数负责报告操作进度的核心工作。它显示 reporter 的提示信息,并附加由 value 计算得出的进度百分比。若百分比为零,或根据 min-change 与 min-time 判断足够接近,则不会在输出中显示百分比。
reporter 必须是调用 make-progress-reporter 返回的对象。value 指定操作的当前状态,取值必须在传入 make-progress-reporter 的 min-value 与 max-value 之间(包含边界)。例如扫描缓冲区时,value 应为调用 point 的结果。
可选参数 suffix 是一个字符串,显示在 reporter 主提示与进度文本之后。若 reporter 为非数值型报告器,则 value 应为 nil,或作为替代 suffix 的字符串。
该函数遵循 make-progress-reporter 中设置的 min-change 与 min-time,因此不会在每次调用时都输出新信息。它执行效率极高,通常无需刻意减少调用次数:过度优化带来的开销往往得不偿失。
该函数与 progress-reporter-update 类似,但会无条件在回显区打印信息。
reporter、value 与 suffix 的含义与 progress-reporter-update 一致。可选参数 new-message 允许修改 reporter 的提示文本。由于该函数始终更新回显区,修改内容会立即呈现给用户。
操作完成时应当调用该函数。它会在回显区显示 reporter 的提示信息,并在末尾添加单词 ‘done’。
你应当始终调用该函数,而不要指望 progress-reporter-update 会显示 ‘100%’。首先,出于多种合理原因它可能永远不会显示百分百;其次,‘done’ 的提示更加明确。
这是一个便捷宏,功能与 dotimes 相同,但会使用上述函数报告循环进度,可减少代码编写量。参数 reporter-or-message 可以是字符串或进度报告器对象。
你可以使用该宏将本节开头的示例改写如下:
(dotimes-with-progress-reporter
(k 500)
"Collecting some mana for Emacs..."
(sit-for 0.01))
若需要为 make-progress-reporter 指定可选参数,使用报告器对象作为 reporter-or-message 参数会很实用。例如可将上例改写为:
(dotimes-with-progress-reporter
(k 500)
(make-progress-reporter "Collecting some mana for Emacs..." 0 500 0 1 1.5)
(sit-for 0.01))
这是另一个便捷宏,功能与 dolist 相同,但会使用上述函数报告循环进度。与 dotimes-with-progress-reporter 一样,reporter-or-message 可以是进度报告器或字符串。你可以使用该宏改写之前的示例:
(dolist-with-progress-reporter
(k (number-sequence 0 500))
"Collecting some mana for Emacs..."
(sit-for 0.01))
有时无法确定操作是否会耗时较长,或实现进度报告器不太方便。这种情况下可以使用该宏。
(with-delayed-message (2 (format "Gathering data for %s" entry)) (setq data (gather-data entry)))
在该示例中,若 body 执行时间超过两秒,就会显示提示信息;若耗时更短则不显示。无论哪种情况,body 都会正常执行,宏的返回值为 body 中最后一个表单的结果。
message 部分会在 body 之前执行,且无论最终是否显示都会被求值。