Emacs 多个“互动”选项

Emacs 多个“互动”选项,emacs,elisp,Emacs,Elisp,我编写了一个小函数,使用逗号作为项目分隔符来反转标记区域中的项目。功能代码为: (取消反向列表(结束) 在位反转列表,其中逗号“,”是列表项分隔符 (交互式“r”) (如果(区域-active-p) (let((区域列表(反向(拆分字符串(区域作为字符串)”,“”))) (结束) (区域列表do中s的循环(程序 (插入(chomp s)) (插入“,”)) (删除字符-2)) (消息“错误:未选择区域!”) 其中,chomp从字符串中去除前导/尾随空格,区域作为字符串将区域作为字符串生成 该功

我编写了一个小函数,使用逗号作为项目分隔符来反转标记区域中的项目。功能代码为:

(取消反向列表(结束)
在位反转列表,其中逗号“,”是列表项分隔符
(交互式“r”)
(如果(区域-active-p)
(let((区域列表(反向(拆分字符串(区域作为字符串)”,“”)))
(结束)
(区域列表do中s的循环(程序
(插入(chomp s))
(插入“,”))
(删除字符-2))
(消息“错误:未选择区域!”)
其中,
chomp
从字符串中去除前导/尾随空格,
区域作为字符串
将区域作为字符串生成

该功能非常有用,但是如果能够动态选择分离器,那就太好了。我要寻找的行为是:

  • 如果在没有通用参数的情况下调用,请使用逗号作为项目分隔符
  • 如果使用通用参数(
    C-u
    )调用,请用户输入一个(可能是多字符)分隔符字符串
我试图做到这一点,但没有成功。如果你能提供帮助,那就太好了

提前感谢,
埃莱马基尔

因为
r
始终是前两个参数。如果使用C-u传递参数。然后,您可能需要将参数作为分隔符传递给第三个参数。但是如果没有第一个和第二个参数,就不能通过第三个参数。(我不确定,我也是初学者)

我希望您使用
r
s
选项来实现它。尽管您始终需要提供分隔符参数

例如:

(defun reverse-list (beg end &optional sepeartor )
  (interactive "r
ssepeartor:")
  (princ beg)
  (princ "*")
  (princ end)
  (princ sepeartor))
您可以使用
p
来读取。如果没有前缀参数,则为
nil
,如果有前缀参数,则为non-
nil
,因此您可以对此进行测试,并决定是否将分隔符设置为
,“
或使用
读取字符串
提示用户输入分隔符

此外,我还要对您的代码发表以下评论:

  • 您的函数只有在以交互方式调用时才能工作,因此
    beg
    end
    实际上是区域的开始和结束。如果以非交互方式调用,这些可能不匹配(或者甚至可能没有区域),在这种情况下,您的函数将出错(因为它删除
    beg
    end
    之间的缓冲区,但插入区域的反转)。因此,您需要调用
    (缓冲区子字符串beg end)
    ,而不是
    (区域作为字符串)


  • [已编辑,请参阅注释]如果区域处于非活动状态,则不建议引发错误。在Emacs中,即使区域处于非活动状态(即不再高亮显示),该区域仍然存在。或者用户可能已经关闭了瞬态标记模式,因此根本没有活动区域。在这两种情况下,用户可能仍然希望运行您的命令

    最多可以在区域处于活动状态时更改命令的行为(例如,请参见)

  • 您可以通过调用
    kill region
    来删除区域,这将删除的文本复制到kill环。这真的是你想做的吗?这可能会让用户感到惊讶。最好调用
    delete region
    ,除非您确实想保存删除的文本

  • 在开始调用
    insert
    之前,您不能确保该点位于正确的位置。在交互式使用中,您可以不受影响,但对于非交互式使用,点可以在任何地方,所以您应该显式地移动它。当然,也可以使用
    保存游览

  • 插入一个额外的
    “,”
    ,然后不得不删除它,这似乎很不雅观。最好不要在一开始就插入它

  • 下面是一些修订后的代码,修复了上述所有问题:

    (defun reverse-list (beg end read-separator)
      "Reverse the region in-place, treating it as a list of items
    separated by commas. With a prefix argument, prompt for the
    separator."
      (interactive "r\nP")
      (save-excursion
        (let* ((separator (if read-separator (read-string "Separator: ") ","))
               (region-list (nreverse (split-string (buffer-substring beg end) separator)))
               (separator (concat separator " ")))
          (goto-char beg)
          (delete-region beg end)
          (loop for s in region-list
                for sep = "" then separator
                do (insert sep) (insert (chomp s))))))
    

    确实是一个更好的解决方案!感谢您的详细评论,我正在学习elisp,因此这非常有帮助!不过有一条评论:起初我没有使用手动错误消息,但后来根本没有发生错误。另外,当我在没有活动区域的情况下使用您的函数时,当前行中截至
    点的内容会反转。。。也许这是我的emacs配置中的一个bug?在添加我的错误处理代码时,您的函数工作得很好。在Emacs中,该区域即使当前未处于活动状态(即突出显示),也会继续存在。请参阅手册中的“”。通常,最好编写命令,以便它们在该区域上运行,即使该区域当前未处于活动状态。毕竟,用户可能已经关闭了
    瞬态标记模式
    ,但仍然希望使用您的命令。+1,重构很好。两个小错误:1
    nreverse
    reverse
    更可取,因为
    split string
    返回一个新列表,因此不需要复制它。2.
    loop
    宏的
    do
    子句已经在隐式
    progn
    中包装了后续表单,因此不需要
    progn
    (defun reverse-list (beg end read-separator)
      "Reverse the region in-place, treating it as a list of items
    separated by commas. With a prefix argument, prompt for the
    separator."
      (interactive "r\nP")
      (save-excursion
        (let* ((separator (if read-separator (read-string "Separator: ") ","))
               (region-list (nreverse (split-string (buffer-substring beg end) separator)))
               (separator (concat separator " ")))
          (goto-char beg)
          (delete-region beg end)
          (loop for s in region-list
                for sep = "" then separator
                do (insert sep) (insert (chomp s))))))