我知道在 gnu emacs 中有几个不同的软件包用于选择多个区域。我正在寻找一种选择多个区域的方法,对它们进行操作,然后选择先前标记的区域的反向,以便对它们进行操作。
例如,假设我在缓冲区中有以下数据:
Line A
Line B
Line C
Line D
Line E
Line F
我想做的是:
- 将行 A、C 和 E 标记为多个区域
- 对这些标记区域内的文本应用一些 elisp
- 告诉 emacs 切换标记和未标记的区域。现在,B、D 和 F 行应该是标记为多个区域的行
- 将一些其他 elisp 应用于这些其他区域中的文本
步骤 1、2 和 4 很简单,它们仅取决于我决定使用的多区域标记代码的选择。
但是第 3 步呢?是否有任何多区域标记包允许我切换到以前标记的反向?
你的问题是一般性的。我的答案可能适合其中的一部分,但可能不是全部。
-
库
zones.el
(请参阅区域(允许您跨多个缓冲区定义和操作文本的多个区域(实际上是多个区域(。 你可以用它们做这些事情:- 对它们进行排序。 合并(
- 合并(相邻或重叠区域(包括对它们进行排序(。
- 如果他们团结起来,就补充他们。
- 相交。
- 将缓冲区缩小到列表中的区域 - 请参阅多重收缩。
- 选择列表中的区域作为活动区域。在区域之间循环。
- 搜索它们(它们首先自动合并(。为此,您需要库
isearch-prop.el
(请参阅 Isearch+(。
突出显示 - 和取消突出显示它们。(为此,您需要突出显示库(
highlight.el
(或库facemenu+.el
(请参阅面部菜单+(。 - 将活动区域添加到区域列表。
- 将区域添加到区域列表,然后合并(合并(区域。
- 从区域列表中删除区域。 将一个区域变量克隆
- 到另一个区域变量,以便克隆具有相同的区域。
- 克隆区域变量,然后合并克隆的区域。
- 在书签中使区域列表变量持久化。使用书签在后续的 Emacs 会话中恢复它。为此,您需要库书签+。
-
库 Isearch+(包括
isearch-prop.el
(允许您搜索由zones.el
定义的区域和由文本或叠加属性(任何属性(定义的区域,并且它有助于将属性应用于文本区域。- 您还可以搜索一组区域的补充,我认为这就是您的问题的真正内容。 在搜索过程中,可以选择将未搜索的区域变暗。
- 您可以将各种"事物"搜索为区域。事物可以是你可以用语法定义的任何事物。它们包括通常的Emacs"事物",如果您使用库
thingatpt+.el
那么您可以轻松定义其他内容。
-
如果使用冰柱,则可以对缓冲区或文件中的一组搜索上下文使用冰柱搜索。您可以通过多种方式定义搜索上下文,包括使用正则表达式。在那里,您也可以搜索一组搜索上下文的补充。您可以使用匹配搜索上下文的增量缩小,并且在此过程中,您还可以使用补充来减去匹配集。
在您发表评论后更新 -
您显然希望补充非基本区域列表(我称之为">izone"(,每个区域都是一个数字作为标识符,后跟两个区域限制。(基本区域只有限制,没有标识符,函数zz-zones-complement
返回基本区域的列表。
这就是我定义补码函数的方式:
(defun zz-complement-izones (izones &optional beg end)
"Complement IZONES, which is a list like `zz-izones'.
Such a list is also returned, that is, zones that have identifiers."
(zz-izones-from-zones
(zz-zones-complement (zz-zone-union (zz-izone-limits izones nil t)))))
这仅在剥离区域标识符并合并区域后使用预定义的功能zz-zones-complement
。 预定义的函数zz-izones-from-zones
为区域标识符提供,使结果成为 izone 列表。
并且您需要一个函数将函数映射到区域列表上,并将其应用于列表中的每个区域 - 您的函数traverse-zones
做什么。
这是此类映射函数的一个版本(未经测试(,它需要您正在使用的形式的区域,即 izone(具有数字标识符的区域(并在它们上映射一个 3 元函数。它类似于您的traverse-zones
(但请参阅评论(:
(defun map-izones (function &optional izones)
"Map 3-ary FUNCTION over IZONES.
FUNCTION is applied to the first three elements of each zone.
IZONES is a list like `zz-izones', that is, zones with identifiers."
;; Do you really want this? It prohibits mapping over an empty list of zones.
;; (unless izones) (setq izones zz-izones)
(when (and (functionp function) (zz-izones-p izones))
(setq izones (zz-unite-zones izones))
(dolist (izone izones) (funcall function (car izone) (cadr izone) (caddr izone)))))
这是一个(未经测试的(版本,它将二进制函数映射到区域列表上。列表中的区域可以是所有基本区域,也可以是所有 izone。 这对我来说似乎更有用,因为在这种情况下,标识符通常不是很有用。
(defun map-zones (function &optional zones)
"Map binary FUNCTION over ZONES, applying it to the limits of each zone.
ZONES can be a list of basic zones or a list like `zz-izones', that
is, zones that have identifiers."
(when (functionp function)
(when (zz-izones-p zones)
(setq zones (zz-izone-limits zones nil 'ONLY-THIS-BUFFER)))
(setq zones (zz-zone-union zones))
(dolist (zone zones) (funcall function (car zone) (cadr zone)))))
希望这有帮助。
我刚刚发现了一个初步答案。我说"初步",因为我还没有做任何深入的测试。但是,"zones.el"包正式提供了我正在寻找的功能。
它创建选定区域的列表,称为"区域"。它提供了一个名为 zz-zones-complement
的函数,它将返回当前区域的"合并"列表的补充,可以按如下方式获取:(zz-zones-complement (zz-zone-union zz-izones))
.
我相信这将为我提供我正在寻找的所有功能。但是,如果我遇到任何问题,我会回来再次发布。
谢谢你,德鲁。
事实证明,区域最接近我需要的,尽管冰柱和 isearch+ 也都很有用。
至于区域,事实证明它的zz-zones-补码函数似乎没有返回正确的信息。
但是,我编写了自己的函数,我可以使用它来代替它。
我现在这样做是为了在以前通过 zones.el 添加的所有区域的补码上运行任意 lisp 代码......
(defun my-complement-zones (&optional zones)
(unless zones
(setq zones zz-izones))
(zz-unite-zones 'zones)
(let ((result ())
(end (copy-marker (point-min)))
(n 0)
(a nil)
(b nil))
(dolist (item (reverse zones))
(setq n (1+ n))
(setq a (cadr item))
(setq b (caddr item))
(setq result (append (list (list n end a)) result))
(setq end b))
(when (< (marker-position end) (point-max))
(setq result (append (list (list (1+ n) end (copy-marker (point-max)))) result)))
result))
;; Each element has three values: an index followed by the start
;; and end markers for each region. To traverse this structure,
;; do the following ...
(dolist (region (my-complement-zones))
(let ((idx (car region))
(start (cadr region))
(end (caddr region)))
;; At this point, "start" is a marker pointing to the
;; beginning of the given zone, and "end" is a marker pointing
;; its endpoint. I can use these for inputs to any region-aware
;; elisp commands, or for any functions that I might want to
;; write which operate on each given region.
;;
;; ... etc. ...
))
。这是我写的一个函数,它将遍历区域列表并将 lambda 应用于每个区域。无论我们使用原始的 zz-izones 还是使用我的函数生成的补码,它都可以等效地工作:
;; Helper function
(defun funcallable (func)
(and func
(or (functionp func)
(and (symbolp func)
(fboundp func)))))
;; Traverse a list of zones such as zz-izones, and apply a lambda
;; to each zone in the list. Works equivalently with the output of
;; `my-complement-zones'.
(defun traverse-zones (func &optional zones)
(when (funcallable func)
(unless zones
(setq zones zz-izones))
(zz-unite-zones 'zones) ;; not sure if this is really necessary
(dolist (zone zones)
(let ((i (car zone))
(s (cadr zone))
(e (caddr zone)))
(funcall func i s e)))))
为了说明 zz-izones 和 zz-zones-补码输出之间的结构差异,下面是我在名为 "foo" 的缓冲区中创建的 zz-izones 结构的示例:
((4 #<marker at 1202 in foo> #<marker at 1266 in foo>) (3 #<marker at 689 in foo> #<marker at 1132 in foo>) (2 #<marker at 506 in foo> #<marker at 530 in foo>) (1 #<marker at 3 in foo> #<marker at 446 in foo>))
以下是(zz-zones-补 zz-izone(对于相同的 zz-izones 列表的样子......
((1 4) (#<marker at 1202 in foo> 3) (#<marker at 689 in foo> 2) (#<marker at 506 in foo> 1) (#<marker at 3 in foo> 1266)
请注意,zz-izones 中的每个条目都包含一个索引和两个标记。但是,在其补码中,每个条目要么是两个整数,要么是一个标记和一个整数。这些结构不是同构的。
附加信息
对于(zz-zone-union (zz-izone-limits zz-izones nil t))
...
((#<marker at 3 in foo> #<marker at 446 in foo>) (#<marker at 506 in foo> #<marker at 530 in foo>) (#<marker at 689 in foo> #<marker at 1132 in foo>) (#<marker at 1202 in foo> #<marker at 1266 in foo>)
对于(zz-zones-complement (zz-zone-union (zz-izone-limits zz-izones nil t)))
((1 #<marker at 3 in foo>) (#<marker at 446 in foo> #<marker at 506 in foo>) (#<marker at 530 in foo> #<marker at 689 in foo>) (#<marker at 1132 in foo> #<marker at 1202 in foo>) (#<marker at 1266 in foo> 1266))
我想如果我将第一个条目中的"1"转换为(copy-marker (point-min))
,将最后一项中的"1266"转换为(copy-marker (point-max))
,我想我可以使用此补码......除非我正在处理特定情况,否则我处理的是标记还是点并不重要。
标记是理想的,因为这样我就可以在生成补码后更改缓冲区,并且我不必担心补码结构中的数字点值不再指向它最初指向的位置。