在前面我们介绍了Region的概念,它就是由 marker 和 point 构成的一个区域。Region 是一种极度临时的状态——我们可以通过移动光标随时修改它的区域,或者激活/取消激活。
- 光标可以随意移动出Region所在区域,以后如果基于这个条件进行操作时容易造成误操作
- Region 可以说只有在激活那一刻才有效,后续如果改变了point 或者 marker 就会造成改变
为了应对这些问题,Eamcs中又有了 narrowing和widen 机制。它能将 Buffer 的可视与操作范围强制限制在特定区域内,为你提供一个安全、持久的局部编辑“沙盒”。
背景知识铺垫
Emacs 中允许我们通过 Narrowing 机制将注意力只集中在 Buffer 的某一部分,而把其余所有的文本暂时“隐身”。这样即使某些操作全局的函数此时也只能操作该范围内的文本。
首先,Narrowing 会改变 (point-max) 、(point-min) 的值,在默认没有 Narrwoing 的情况下, point-min 总是返回1,而 point-max 返回buffer中最后一个字符所在的索引。但一旦对 Buffer 施加了 Narrowing, point-min 、point-max 的值就与我们设置的可见区域的区间有关了。
我们可以通过 (narrow-to-region START END) 将buffer的可见区域设置为从 START 到 END之间的区域。通过 (widen) 来解除所有之前设置的 narrowing。
实际上于 mark 、point 类似,对于 Narrowing 我们也要考虑备份和恢复现场的情况,保存和恢复现场使用的函数是 save-restriction。在一些插件的代码中我们经常看到 save-restriction 和 save-excursion 成对出现。它们的用法也是类似的。
既然它们一般是成对出现的,我们不妨考虑一下它们两个谁在外层,谁在内层呢?
假如 save-restriction 在外层,而 save-excursion 在内层, 也就是这样
;; 假设当前光标位于位置 (Point = 400)
(save-restriction
(save-excursion
(widen) ;; 暂时恢复可见区间为整个buffer
(narrow-to-region (point-min) 200)
(goto-char (point-max))
;; 执行其他操作
))上述例子中,我们通过 narrow-to-region 将可见区域限制在 1 到 200 这个区间。然后执行一些操作。当要恢复时先调用 save-excursion 尝试恢复光标位置到最初的 400时发现它的可见区域最多到200,此时光标位置可能被置于200位置。最后执行 save-restriction 恢复可见区域,但是此时光标已经回不到最初的 400位置了。
从上面可以看到最好是将 save-excursion 放在外层。
练习
本次的例子,我们实现一个简单的统计指定行中单词的个数。这次用到的函数为 count-words , 它的原形如下:
(count-words START END &optional TOTALS)它主要接收两个参数:统计的起始位置和结束位置。
获取行的区间坐标可以通过 forword-line 移动光标到指定行,然后通过 line-beginning-position 和 line-end-position 获取行首和行尾位置的 point值
利用今天所学的内容可以写下如下的代码:
(defun my/count-words-in-line(line)
;; 获取行首和行尾位置
(save-excursion
(save-restriction
(widen) ;; 先取消之前所设置的可见区间恢复整个buffer可见
(goto-char (point-min))
(forward-line (1- line)) ;; 移动到对应行
(narrow-to-region (line-beginning-position) (line-end-position))
(count-words (point-min) (point-max))
)))总结
本次我们了解了 Emacs 中的 Narrowing与Widen 机制。后面我们写代码操作文本时可以先直接默认操作整个区域,而在需要关注操作区间时通过 narrow-to-region 设置可见区间。不过不要忘了通过 save-restriction 恢复之前设置的可见区间。
本次我们介绍的api函数有:
narrow-to-region设置可见区间widen取消之前设置的所有可见区间save-restriction恢复调用之前的可见区间
评论 (0)