【问题标题】:Emacs: select multiple regions and switch to inverseEmacs:选择多个区域并切换到反向
【发布时间】:2016-02-06 16:18:58
【问题描述】:

我知道在 gnu emacs 中有几个不同的包用于选择多个区域。我正在寻找一种方法来选择多个区域,对其进行操作,然后选择先前标记的区域的倒数以便对它们进行操作。

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

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

我想做的是:

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

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

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

【问题讨论】:

    标签: emacs elisp inverse regions


    【解决方案1】:

    您的问题很笼统。我的回答可能适合其中的一部分,但可能不是全部。

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

      • 对它们进行排序。
      • 联合(合并)相邻或重叠的区域(包括对它们进行排序)。
      • 如果他们已经团结,就补充他们。
      • 与它们相交。
      • 将缓冲区缩小到列表中的区域 - 请参阅 MultipleNarrowings。
      • 在列表中选择区域作为活动区域。在区域之间循环。
      • 搜索它们(它们首先自动合并)。为此,您需要库 isearch-prop.el(请参阅 Isearch+)。
      • 突出显示和取消突出显示它们。 (为此,您需要 Highlight 库 (highlight.el) 或库 facemenu+.el(请参阅 Facemenu+)。
      • 将活动区域添加到区域列表中。
      • 将该区域添加到区域列表中,然后合并(合并)这些区域。
      • 从区域列表中删除一个区域。
      • 将一个区域变量克隆到另一个变量,因此克隆具有相同的区域。
      • 克隆一个区域变量,然后合并克隆的区域。
      • 书签中使区域列表变量持久化。使用书签在随后的 Emacs 会话中恢复它。为此,您需要库 Bookmark+
    2. Isearch+(包括isearch-prop.el)可让您搜索zones.el 定义的区域和文本或覆盖属性(任何属性)定义的区域,它有助于将属性应用于文本区域。

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


    在您发表评论后更新 -

    您显然想要补充一个 基本区域列表(我称之为“izones”),每个区域都是一个数字作为标识符,后跟两个区域限制。 (基本区域只有限制,没有标识符,函数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 所做的。

    这里是这样一个 map 函数的一个版本(未经测试),它需要您正在使用的形式的区域,即 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)))))
    

    希望这会有所帮助。

    【讨论】:

    • 我们的帖子交叉了 - 当我正在输入我的帖子时,您发布了关于 zones.el 的答案。 ;-)
    • 感谢您所做的一切。不过,我还有另一个问题。您写了“基本区域只有限制,没有标识符,函数 zz-zones-complement 返回基本区域列表”。我明白那个。那么,如果不是 zz-izones 结构本身,我应该将什么参数传递给 zz-zones-complement?换句话说,在给定当前 zz-izones 结构的情况下,如何获得“基本区域”列表?同样,我的目标是:我想为“正常”区域和同构的补充区域使用结构。
    • 在下面查看我刚刚添加的“附加信息”部分。
    • 您可以使用zz-izone-limits 函数从zz-izones 等结构中获取基本区域列表。它只是删除标识符,只留下每个区域的限制。
    【解决方案2】:

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

    它会创建一个选定区域的列表,称为“区域”。它提供了一个名为zz-zones-complement 的函数,它将返回当前区域的“合并”列表的补集,可以通过以下方式获得:(zz-zones-complement (zz-zone-union zz-izones))

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

    【讨论】:

      【解决方案3】:

      谢谢你,德鲁。

      事实证明,zones 最接近我的需要,尽管 icicles 和 isearch+ 也都很有用。

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

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

      我现在这样做是为了在之前通过 zone.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)),我想我可以使用这个补码......除非我正在处理特定情况我处理的是标记还是点都没有关系。

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

      【讨论】:

      • 你没有说zz-zones-complement有什么问题。考虑提交错误报告或增强请求(再次下载文件,获取联系信息)。谢谢。
      • zz-zones-complement 返回一个不是有效的“zz-izones”结构的结构。这个返回值不能用于标准“zz-izones”工作的许多地方。我自己的结构(来自 my-complement-zones)确实可以在任何地方使用“zz-izones”。我确实会向作者报告。
      • 谢谢。期待报告。我不认为这是一个问题,先验的。请参阅isearch-prop.el,了解如何将它与zz-izones 或任意区域变量一起使用。例如,(let* ((zones (zz-zone-union (zz-izone-limits (symbol-value variable) nil 'ONLY-THIS-BUFFER))) (comp-zones (zz-zones-complement zones))) (dolist (zone (if isearchp-complement-domain-p comp-zones zones)) ...)
      • 哦...看起来您可能是作者...至少您有相同的名字!如果是这样,您已经有了我的解决方法。我希望 zz-izones 的补码与 zz-izones 本身具有相同结构的原因是因为我想在原始码和补码之间来回切换,并且我不想处理这两种结构不同。例如,我写了一个 traverse 函数,我将在此处发布,它可以与原始补码和我生成的补码一起使用。
      • ... 我还发布了一个 zz-izones 示例的内容和 (zz-zones-complement zz-izones) 的结果。您可以看到这两个列表具有不同的结构。
      猜你喜欢
      • 2023-04-08
      • 1970-01-01
      • 2022-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-03
      • 1970-01-01
      • 2018-06-08
      相关资源
      最近更新 更多