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

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

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

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

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

1赞

感觉这翻译一般。可惜我不是学中文的,想不出来更好的翻译。escape-edit可能更准确,但难理解其功能;comment-edit能一眼看出来能做什么,不失为一个好名字。

昨天思路不太对,反而把事情搞复杂了。现在给 comment-edit 加了一个参数,直接输入编辑区域(甚至可以考虑手动选择), 这样就可以应对 Commentary 以及类似场景了:

(defun comment-edit-el-commentary ()
  "Edit elisp Commentary section."
  (interactive)
  (let* ((comment-edit-default-mode 'markdown-mode)
         (comment-edit-leave-blank-line-in-comment t)
         (beg (save-excursion
                (goto-char (point-min))
                (re-search-forward "^;;; Commentary:\n+" nil t)))
         (end (save-excursion
                (goto-char beg)
                (re-search-forward "\n;;; .*$" nil t)
                (goto-char (match-beginning 0)))))
    (comment-edit (list :beginning beg :end end))))

EDIT: 1.调整匹配规则,忽略头部空行,2.无需将光标移动到 Commentary 区域内。

1赞

回去更新一下试试

现在全面支持递归(之前只实现了字符串的递归 unscape)进入了:

+--------+   C-c '   +--------+   C-c '   +--------+   C-c '
| source | --------> | edit   | --------> | edit   | --------> ...
| buffer | <-------- | buffer | <-------- | buffer | <-------- ...
+--------+  C-c C-c  +--------+  C-c C-c  +--------+  C-c C-c

不过嵌套多了会晕,两次应该是日常比较用的到的:

source buffer -> edit buffer (markdown/orgmode) -> code block

1赞

乞丐版 el2readme:

(defun el-commentary-to-readme-md ()
  "Write elisp Commentary section to README.md."
  (interactive)
  (let* ((beg (save-excursion
                (goto-char (point-min))
                (re-search-forward "^;;; Commentary:\n+" nil t)))
         (end (save-excursion
                (goto-char beg)
                (re-search-forward "\n;;; .*$" nil t)
                (goto-char (match-beginning 0))))
         (str (buffer-substring-no-properties beg end)))
    (with-temp-buffer
      (insert str)
      (comment-edit--remove-comment-delimiter
       (comment-edit--comment-delimiter-regexp 'emacs-lisp-mode))
      (write-file (expand-file-name "README.md")))))

可以直接用lm-commentary(from builtin lisp-mnt)获取Commentary,不用自己手动搜索了。

2赞

刚刚想到 local variables 简单用 ``` 包裹起来就可以在 emacs-lisp-mode 下编辑(冒号不影响缩进):

;; ```
;; Local Variables:
;; ...
;; End:
;; ```

或专门写个 comment-edit-el-local-variables 函数来处理 local variables 块 (加上之前的 commend-edit-el-commentaryel-commentary-to-readme-md 一并重新整理如下):

(defun comment-edit/region-between-regexps (begin-regexp end-regexp)
  (save-excursion
    (goto-char (point-min))
    (when (re-search-forward begin-regexp)
      (let ((begin (point)))
        (when (re-search-forward end-regexp nil t)
          (goto-char (match-beginning 0))
          (cons begin (point)))))))

(defun comment-edit/edit-region (region edit-buffer-mode &optional leave-blank-p)
  (let* ((comment-edit-default-mode edit-buffer-mode)
         (comment-edit-leave-blank-line-in-comment leave-blank-p))
    (comment-edit (list :beginning (car region)
                        :end       (cdr region)))))

(defun comment-edit-el-commentary ()
  (interactive)
  (comment-edit/edit-region
   (comment-edit/region-between-regexps "^;;; Commentary:\n+" "\n;;; .*$")
   'markdown-mode
   t))

(defun comment-edit-el-local-variables ()
  (interactive)
  (comment-edit/edit-region
   (comment-edit/region-between-regexps "^;; Local Variables:\n+" "\n;; End:$")
   'emacs-lisp-mode))

(defun el-commentary-to-readme-md ()
  (interactive)
  (let ((filename (expand-file-name "README.md")))
    (when (yes-or-no-p (format "%s %S? "
                               (if (file-exists-p filename)
                                   "Overwrite"
                                 "Create")
                               filename))
      (let* ((reg (or (comment-edit/region-between-regexps "^;;; Commentary:\n+" "\n;;; .*$")
                      (error "Commentary not found in current file!")))
             (str (buffer-substring-no-properties (car reg) (cdr reg))))
        (with-temp-buffer
          (insert str)
          (comment-edit--remove-comment-delimiter
           (comment-edit--comment-delimiter-regexp 'emacs-lisp-mode))
          (write-file filename))))))

试用了一下,非常棒!

rust 时正好可以使用这个插件较美观的写 doc comment 了。

不过有一个问题,在 indirect-edit buffer 里的空白行也会在源文件里尾部添加一个空格,如图 image

我发了一个 pr 来可选去除尾部的空格了

EDIT: pr

1赞

写了个简单函数,从Elisp文件中直接提取Commentary生成README。

(require 'subr-x)
(require 'org)
(require 'pcase)
(require 'lisp-mnt)

(defun limon-doc--sanitize-commentary (commentary)
  "Clean up the COMMENTARY returned from `lm-commentary' for export."
  (let ((bodies (cdr (split-string (string-trim commentary) "\n")))
        (result nil))
    (dolist (s bodies)
      (pcase s
        ((or ";;" ";; ") nil)
        ((rx bol ";; " (let rest (1+ any))) (push rest result))
        (some (push some result))))
    (string-join (nreverse result) "\n")))

(defun limon-doc-export-commentary (dest)
  "Export Org-formatted Commentary section in this buffer to DEST."
  (interactive "F")
  (pcase (lm-commentary)
    (`nil (user-error "No valid commentary section in this buffer!"))
    (some
     (let ((body (limon-doc--sanitize-commentary some))
           (summary (lm-summary)))
       (with-temp-buffer
         (insert body)
         (org-export-to-file 'org dest nil nil nil nil `(:title ,summary)))))))
1赞

前面的自定义函数 comment-edit-el-local-variables 可以删除了,comment-edit:77bc3d1 现在已经可以识别 Local Variables 块,并且以 emacs-lisp-mode 作为编辑模式:

Screenshot_2020-02-14_at_4.32.04_PM——comment-edit-local-variables-block

可以加一个 feature 吗?

例如这样

class Foo {
    /// 这是一个加一操作
    /// 当前 buffer 的 fill-column 为 80, 如果在 comment-edit 编辑时,自动计算
    /// 好 comment-edit 里的 fill-column 为 80 - (4空格 + 3个/ + 1个空格) = 72
    /// 这样只需要在 comment-edit 的 buffer 里调用一下 fill-paragraph 就可以排版好而无需写完
    /// documentation comment 之后再排版一下。
    int add1(int x) {
        return x + 1;
    }
};

你把这个 https://github.com/twlz0ne/comment-edit.el/issues/3 重新打开。

我稍后看看怎么实现。

lentic后来我也放弃了,有一些特殊情况需要注意,每次都用的小心翼翼。。。

我是习惯直接在el文件中裸写 org 格式的 commentary 然后生成 readme.md

1赞

功能已经好了,待我明天提交

用法是这样:

(require 'comment-edit)

;; 继续源 buffer 没有用完的 fill-column 宽度
(setq comment-edit-continue-fill-column t)

;; 在 hook 中决定如何使用 fill-column 或者做更多的事
(add-hook 'comment-edit-buffer-creation-hook #'auto-fill-mode)

剩余 fill-column 有可能计算错误,这取决于快首空白行的情况。如需修正,也可以自行在 hook 中完成。

2赞

增加了手动选择编模式的支持。

例如想要把字符串 "console.log(\"foo\") 当作 js 代码编辑,然而当前上下文没有足够的信息让 comment-edit 去猜测该使用何种模式。

这时就需要手动模式了,按 C-u C-c ’ 即可在进入编辑之前选择模式。

改名之心日盛。

昨晚一不小心又通宵了,顺便想了几个名称:

  • transient-edit 表示短暂离开当前模式,临时进入到另一种编辑状态
  • separedit 取 separate 前半部,表示隔离、分开编辑的意思
  • ripedit 表示把内容剥离出来编辑

目前比较满意的是 separedit,应该不会有歧义。

期待上 melpa