《elisp 每日一练》——minibuffer输入读取和set-transient-map 临时热键状态机

《elisp 每日一练》——minibuffer输入读取和set-transient-map 临时热键状态机

Masimaro
2026-09-02 / 0 评论 / 1 阅读 / 正在检测是否收录...

我们在前面介绍了 completing-read 给用户提供选项,让用户输入,但是如何处理这种需要用户主动输入的情况呢?本节我们将介绍一些主动读取用户输入的接口,然后补充一个我们在前面练习中涉及到的函数 set-transient-map

背景知识

completing-read 主要是提供几个候选项供用户选择,有点类似一般GUI程序的下拉列表框。而我们今天介绍的 read-from-minibuffer 就像GUI程序中的文本框。

它的函数签名如下:

(read-from-minibuffer PROMPT &optional INITIAL-CONTENTS KEYMAP READ HIST DEFAULT-VALUE)

其中常用参数的含义如下:

  • PROMPT 提示词
  • INITIAL-CONTENTS 预先填写的内容,后续可以修改
  • READ 如果为 t,会自动调用 Lisp Reader,将输入的文本直接解析为 Lisp 对象
  • DEFAULT-VALUE 当用户直接按回车时返回的字符串

该接口相对来说是一个偏底层的接口,如果程序预先指定用户输入数据的类型,可以选择一些相对上层的封装函数:

  • read-string 读取纯文本
  • read-number 读取数字
  • read-command 读取一个命令
  • read-file-name 读取一个文件名
  • read-directory-name 读取一个目录名称

我们可以通过 read 作为前缀搜索类似的函数。

另外一个今天要补充的函数就是 set-transient-map

(set-transient-map KEYMAP &optional KEEP-PRED ON-EXIT)

它会临时将对应的keymap挂起,在下一次按键之后,会调用 KEEP-PRED 参数绑定的函数返回值决定是否卸载keymap。它的函数返回值有三种情况:

  • nil 执行一个命令后理解卸载,也就是一次性的
  • t 保持,直到下一次调用时该函数返回nil
  • 数字 如果是数字值,保持激活状态,直到连续执行了指定次数的命令后自动卸载

练习

今天我们的练习,我们来实现一个快速的文本大小写与替换功能。它的功能如下:

  • 利用region 选中一段文本
  • C-c t 进入该模式,此时我们利用 set-tranient-map 挂载一个快捷键的keymap
  • u 将选中文本转为大写
  • l 转为小写
  • c 转为首字母大写(Captial)
  • r 调用 read-string 读取你要替换的字符串,然后替换选中区域
  • q 或任意其他键退出

我们先定义这些功能的函数

(defun my/upcase-region()
  "选中区域的文本转化为大写"
  (interactive)
  (when (use-region-p)
    (upcase-region (region-beginning) (region-end))))

(defun my/downcase-region()
  "选中区域文本转为小写"
  (interactive)
  (when (use-region-p)
    (downcase-region (region-beginning) (region-end))))

(defun my/capitalize-region()
  "选中区域首字母大写"
  (interactive)
  (when (use-region-p)
    (capitalize-region (region-beginning) (region-end))))

(defun my/replace-region()
  "替换选中区域的文本"
  (interactive)
  (when (use-region-p)
    (let* ((old-text (buffer-substring (region-beginning) (region-end)))
           (new-text (read-string (format "替换 '%s' 为:" old-text))))
      (delete-region (region-beginning) (rregion-end))
      (insert new-text))))

然后我们可以定义一个keymap,将这些操作进行绑定

(defvar my/text-operator-transient-map
  (let ((map (make-sparse-keymap)))
    (keymap-set map "u" #'my/upcase-region)
    (keymap-set map "l" #'my/downcase-region)
    (keymap-set map "c" #'my/capitalize-region)
    (keymap-set map "r" #'my/replace-region)
    (keymap-set map "q" (lambda()
                (interactive)
                (set-transient-map nil)))
    map))

最后我们绑定一个快捷键进入该模式

(defun my/start-text-operate()
  (interactive)
  (if (use-region-p)
      (set-transient-map my/text-operator-transient-map)
    (message "请先选中一段文本")))

(keymap-global-set "C-c t" #'my/start-text-operate)

小结

本篇我们介绍了如何读取minibuffer 中用户的输入,以及补充了一个关于快捷键绑定的额外函数。本节涉及到的函数有:

  • read-from-minibuffer 以及基于该函数实现的read 系列函数
  • set-transient-map
0

评论 (0)

取消