我们在前面介绍了 completing-read 给用户提供选项,让用户输入,但是如何处理这种需要用户主动输入的情况呢?本节我们将介绍一些主动读取用户输入的接口,然后补充一个我们在前面练习中涉及到的函数 set-transient-map
背景知识
completing-read 主要是提供几个候选项供用户选择,有点类似一般GUI程序的下拉列表框。而我们今天介绍的 read-from-minibuffer 就像GUI程序中的文本框。
它的函数签名如下:
emacs-lisp
(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
emacs-lisp
(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` 或任意其他键退出
我们先定义这些功能的函数
```emacs-lisp
(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,将这些操作进行绑定
emacs-lisp
(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))
最后我们绑定一个快捷键进入该模式
emacs-lisp
(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