[分享] separedit.el (原 comment-edit.el): 在单独的缓冲区编辑注释、docstring 或其中的代码块

这个扩展其实在几个月前讨论《 如何善用注释/文档中的代码? 》的时候就差不多了(当时没实现的功能现在也没😅)。

一直捂着,不如放出来,集思广益,慢慢改进。

现在整理了一下,并把包名由 commentdown 改为 separedit,一是因为并没有以 markdown-mode 作为编辑主模式,二是保留将来支持 orgmode 的可能(不被名称限定)

关于注释块/行概念的厘清

不以注释符号来区别「块」和「行」,因为 /* */ 也可以是行注释。而是以是否连续作为判断依据。

在讨论「连续性」之前,先给 /* */// 两种注释赋予新的解释:

  • /* */ 封闭型注释。自带边界,隔绝外部。
  • // 开放型注释。以空行和左侧代码为界,两两相邻集结成块。

所以,是否连续就要看上下两行首尾之间有无被代码/空行所阻断,例如:

/*
 * 这是
 * 注释块
 */
 
//
// 这也是
// 注释块
//

//
// 这另一个
// 注释块
//

code 1 // 这些都是行注释
code 2 // 这些都是行注释
code 3 /* 这些都是行注释 */
code 4 /* 这些都是行注释 */

有时候需要忽略空行,把多个注释块(例如 Elisp 文件的 Commentary)作为整体来编辑?参见:#22 楼

用这个包的好处

  • 可以使用 markdown / orgmode 或任何你喜欢的 mode 编辑注释、字符串和 docstring,或直接编辑其中的代码块,separedit 会去掉包裹在编辑内容之外的字符,例如:

     在注释或 docstring 中              在编辑模式中
    
     // ```
     // this is a                       this is a
     // code block                      code block
     // ```
    
     // ,---
     // | this is a                     this is a
     // | code block                    code block
     // `---
    

    编辑完,返回,恢复包裹字符。

    • 编辑时自动判定所需的 mode,如果判断不出则沿用当前的 major-mode,还可以手动选择。
    • 如果编辑的块(无论是注释块还是代码块)中有嵌套,可以再次开启新的 edit buffer 进行编辑(理论上不限制嵌套层数,但层数多了容易头晕)。
  • 编辑 docstring 时免受引号(包括单引号)转义之累。例如:

      在 docstring 中                   在编辑模式中
      
      (foo \"bar\")                     (foo "bar")
    

    同样也支持嵌套的转义字符编辑。


ChangeLog:

  • 支持单双引号嵌套转义 #11
  • 支持直接传递 block 给编辑函数,以实现编辑类似 elisp commentary section 的区块 #22
  • 全面支持递归 #24
  • 支持Local Variables: 块的编辑 #30
  • 支持 fill-column 接力 #35
  • 支持手动选择编辑模式 #36
  • 更名为 separedit.el
  • melpa 审核通过,增加 -preserve-string-indentation 选项 #56
  • 增加 help-mode helpful-mode 支持,修复了进入编辑/提交返回之后光标位置不固定、以及部分情况下空格丢失的问题 #68
7赞

老铁真是高产啊

都是些无关紧要的 idea

以前看tumashu推荐过lentic,和本贴所要达到的目标类似,不过太高端了,至今我不会用。。

2赞

有装类似的 string-edit,一直想不起来用。

这个厉害了,只是我感觉平时都不这么用呢

我只是想要在Commentary里用org-mode写个README而已 :joy: 因为Melpa的default recipe居然只能抓取Texinfo格式的文档

tumashu还写了专门提取Commentary的插件

这个很强大。不知道它在进入编辑状态( org-mode ) 之后,能否再次进入 code block 的编辑?

没看到有 docstring 的例子?

例子太少了,所以根本不知道怎么用

@tumashu 能不能指教一下?

另外有点讨厌的是他不能直接复用Elisp文件原有的outline而是要自己再弄一层outline

总觉得把简单的事给复杂化了,可能是不会用吧 :joy::joy::joy:

刚刚试了一下 string-edit,发现它对 js 字符串单/双引号嵌套使用和 python docstring 支持不好。

我参考 string-edit 修复了 comment-edit--string-region 函数结果不准确的问题,并把上述需求也一并解决了。

1赞

多层嵌套的转义符号竟然是指数增长(原以为是x2):

(cl-labels ((escape
             (sl)
             (cond ((not sl) "")
                   ((stringp sl) (format "%S" sl))
                   (t (format "%S" (concat (car sl) (escape (cadr sl))))))))
  (insert
   (escape '("a" ("b" ("c" ("d" ("e" ("f" ("g" ("h" "i")))))))))))

;; => "a\"b\\\"c\\\\\\\"d\\\\\\\\\\\\\\\"e\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"f\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"g\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"h\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\"i\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"\\\\\\
;;    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"\\\\\\\\\\\\
;;    \\\\\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\"\\\"\""

试了一下 4 层转义(再多层就晕了),层层进入编辑(代码还没提交到 github)时的缓冲区内容是这样的:

| n | *edit buffer*                                                       |
|---|---------------------------------------------------------------------|
| 0 | "a\"b\\\"c\\\\\\\"d\\\\\\\\\\\\\\\"e\\\\\\\\\\\\\\\"\\\\\\\"\\\"\"" |
| 1 | "b\"c\\\"d\\\\\\\"e\\\\\\\"\\\"\""                                  |
| 2 | "c\"d\\\"e\\\"\""                                                   |
| 3 | "d\"e\""                                                            |
| 4 | "e"                                                                 |

编辑完了再一层一层提交回去。

1赞

用到了edit-indirect,最好在readme里说一下。


编辑C 的 block comment 的话,星号后面不会自动加入空格:

之前:

/*
 *
 */

编辑buffer:

Test

之后:

/*
 *Test
 */

不过如果本来就有文字的话就没问题: 之前:

/*
 * Test
 */

之后

/*
 * Test
 * Add
 */

我是想着以后要上 melpa 就没写。或许也可以加一个首次运行提示并自动安装的函数,就像 markdown-mode:

这个问题应该在我昨天晚上这个 https://github.com/twlz0ne/comment-edit.el/commit/f7c6366d89bc651e1e9f77d968fe9f658c6bc0b7 提交修复了。

总是有一些边角/边界的问题不好处理。

这两天都在思考怎么解决,期间还写了一版用 syntax-ppss 来定位边界的代码,后来发现很不靠谱,很多 mode 下表现不一致 :sweat:

尴尬了,发现一个同名的扩展 https://www.emacswiki.org/emacs/comment-edit.el

支持这种方框包围的注释:

;; Style n. 3: "narrow-box"
;; 
;;     ##################
;;     # comment line 1 #
;;     # comment line 2 #
;;     # comment line 3 #
;;     ##################
;; 
;; Style n. 4: "wide-box"
;; 
;;     ########################################################################
;;     # comment line 1                                                       #
;;     # comment line 2                                                       #
;;     # comment line 3                                                       #
;;     ########################################################################

不过它仅限于 shell-like 类的注释,不支持 /* */ 以及其它形式的注释,也不支持 docstring 和注释里的 code block,我释然了。

2赞

还有 poporg, org-commentary outorg 等等的,只不过是用Org Mode编辑模式。我 自己就经常用 poporg. 还是很好用的,尤其是对于我这样的Org Mode重度使用者。

1赞

Commentary 里写 README 是个好注意。

Commentary 里边的空行是否必须留白?如果不是必须,现在就可以用 comment-edit 来编辑,只需把 comment-edit-default-mode 设为 org-mode

(defun comment-edit-el-commentary ()
  (interactive)
  (let ((comment-edit-default-mode 'org-mode))
    (comment-edit)))

如果希望保持空行留白,目前有两个问题需要解决:

  1. comment-edit 以连续的 ;; 作为编辑块,遇到空行则认为块结束。

    解决方案:设置一个 greedy 开关,为 t 时可以跨过空行搜索。

  2. 编辑完了之后,提交的时候会在每一行(包括空行)前面添加注释符 ;;

    解决方案:允许提交函数忽略空行。

待我明天来看看怎么实现。

如果用el2org生成README,是不需要对空白行额外加;;

pyim的源文件是一个例子,不过他那个太复杂了,类似文学编程。我用的是简化后的框架,只用来写README

https://github.com/cireu/emacsql-sqlite3/blob/master/emacsql-sqlite3.el#L28

我比较倾向于写完readme自动插入到el文件commentary里。不过comment-edit一包多用也不错

忽然想到,这个包应该改名为 escape-edit。港台把 escape 翻译作「跳脱」,我认为更准确。

字符串引号加斜杠叫跳脱,那么加注释符和 code block 包裹符可视为另一种层次的跳脱。

干着 escape 字符串的活,名称却叫 comment-edit,有一种词不达意、强行凑功能的感觉。

1赞