Emacs:select 多个区域并切换到反向

Emacs: select multiple regions and switch to inverse

我知道 selectgnu emacs 中的多个区域有几个不同的软件包。我正在寻找的是 select 多个区域的方法,对它们进行操作,然后 select 先前标记区域的倒数,以便对它们进行操作。

举个例子,假设我在缓冲区中有以下数据:

Line A
Line B
Line C
Line D
Line E
Line F

我想做的是:

  1. 将 A、C 和 E 行标记为多个区域
  2. 对这些标记区域内的文本应用一些 elisp
  3. 告诉emacs 切换标记和未标记区域。现在,B、D 和 F 行应该是标记为多个区域的行
  4. 对这些其他区域内的文本应用一些其他的 elisp

步骤 1、2 和 4 很简单,它们仅取决于我决定使用的多区域标记代码的选择。

但是第 3 步呢?是否有任何多区域标记包可以让我切换到之前标记的反向?

我刚刚发现了一个初步的答案。我说 "preliminary",因为我还没有做任何深入的测试。但是,"zones.el" package 正式提供了我正在寻找的功能。

它创建了一个选定区域的列表,它称之为 "zones"。它提供了一个名为 zz-zones-complement 的函数,它将 return 当前区域的 "coalesced" 列表的补充,可以通过以下方式获得:(zz-zones-complement (zz-zone-union zz-izones)).

我相信这将为我提供我正在寻找的所有功能。但是,如果遇到任何问题,我会回来并再次 post。

你的问题很笼统。我的回答可能适合其中的一部分,但可能不适合所有。

  1. zones.el (see Zones) 允许您跨多个缓冲区定义和操作文本的多个区域(实际上是多个区域)。你可以用它们做这些事情:

    • 对它们进行排序。
    • 合并(合并)相邻或重叠的区域(包括对它们进行排序)。
    • 如果统一了就补上
    • 将它们相交。
    • 将缓冲区缩小到列表中的区域 - 请参阅 MultipleNarrowings。
    • Select 列表中的区域作为活动区域。在区域之间循环。
    • 搜索他们(他们先自动合并)。为此你需要图书馆 isearch-prop.el (see Isearch+).
    • 突出显示和取消突出显示它们。 (为此你需要 Highlight library (highlight.el) or library facemenu+.el (see Facemenu+)。
    • 将活动区域添加到区域列表。
    • 将区域添加到区域列表,然后联合(合并)区域。
    • 从区域列表中删除一个区域。
    • 将一个区域变量克隆到另一个区域变量,因此克隆具有相同的区域。
    • 克隆一个区域变量,然后合并克隆的区域。
    • 书签中使区域列表变量持久化。使用书签在后续 Emacs 会话中恢复它。为此,您需要库 Bookmark+.
  2. Isearch+ (including isearch-prop.el) lets you search both zones as defined by zones.el 和由文本或覆盖属性(任何属性)定义的区域,它有助于将属性应用于文本区域。

    • 您还可以搜索一组区域的补充,我认为这才是您真正要问的问题。在搜索过程中,可以选择将未搜索的区域变暗。
    • 您可以搜索各种 "THINGS" 作为区域。事物可以是任何你可以在语法上定义的东西。它们包括常用的 Emacs "things",如果您使用库 thingatpt+.el,那么您可以轻松定义其他内容。
  3. 如果您使用 Icicles,那么您可以在缓冲区或文件中的一组 搜索上下文 上使用 Icicles 搜索。您可以通过多种方式定义搜索上下文,包括使用正则表达式。您也可以在其中搜索一组搜索上下文的 补充 。您可以使用增量缩小匹配搜索上下文,并且在这样做时您还可以使用补充来减去匹配集。


在您发表评论后更新 -

你显然想补充一个基本区域列表(我称之为“izones”),每个区域都是数字作为标识符,后跟两个区域限制。 (基本区域只有限制,没有标识符,函数zz-zones-complement returns基本区域列表。)

这就是我定义补函数的方式:

(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 的作用。

这是此类地图函数的一个版本(未经测试),它需要您正在使用的形式的区域,即 izones(具有数字标识符的区域)并在其上映射一个 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)))))

希望对您有所帮助。

谢谢你,德鲁。

事实证明zone最接近我需要的,虽然icicles和isearch+也很有用。

至于区域,事实证明它的 zz-zones-complement 函数似乎 return 不正确的信息。

但是,我编写了自己的函数,可以代替它使用。

我现在对 运行 以前通过 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-complement 的输出之间的结构差异,这里是我在名为 "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-izones 列表的 (zz-zones-complement 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)),我想我可以使用这个补语……除非我我正在处理一个具体案例,在这个案例中,我处理的是标记还是点都无关紧要。

标记是理想的,因为这样我可以在生成补码后更改缓冲区,而且我不必担心补码结构中的数字点值不再指向它原来指向的位置。