如何Elisp实现返回一个region选择的行数列表?

当我region选中一个区域时,我想要返回一个选中的行数的列表。 可参考的函数 line-number-at-pos, region-beginning, region-end, etc 我是想要写一个macro,接受三个参数,用于在每一个行上面执行一个函数。 下面是我已经写了一点点的雏形。

(defgroup comment-annotate-mode nil
  "Comment-annotate-mode options."
  :prefix "comment-annotate-"
  :group 'comment-annotate-mode)

(defcustom comment-annotate-style 'default
  "Comment-annotate-mode style to be used."
  :type 'symbol
  :safe #'symbolp
  :group 'comment-annotate-mode)

(defmacro comment-annotate--loop-over-lines (start end func)
  "Loop over region of START and END selected lines and apply FUNC."
  (mapc
   (lambda (line)
     (goto-line line)
     (func))
   ;; TODO line numbers list
   ))

(defun comment-annotate--max-length (start end)
  "Find out the longest line length between region START and END.
It will to be used as base position for comment beginning point."
  (comment-annotate--loop-over-lines
   )
  (apply #'max
         (mapcar #'length
                 (split-string
                  (buffer-substring-no-properties
                   (save-excursion
                     (goto-char start)
                     (line-beginning-position))
                   (save-excursion
                     (goto-char end)
                     (line-end-position)))
                  "\n"))))

(defun comment-annotate--insert-comment-char (block-start block-end max-length)
  "Insert a vertical line.of comment char and bar line character."
  (save-excursion
    (comment-annotate--loop-over-lines
     )))

(defun comment-annotate-region (start end)
  "Annotate region between START and END with comment."
  (interactive "r")
  (let ((block-start (save-excursion (goto-char start) (line-number-at-pos)))
        (block-end (save-excursion (goto-char end) (line-number-at-pos)))
        (max-length (comment-annotate--max-length start end)))
    (comment-annotate--insert-comment-char block-start block-end max-length)))

(defvar comment-annotate-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "i") 'comment-annotate-insert)
    map)
  "Comment-annotate-mode map.")

(define-minor-mode comment-annotate-mode
  "Annotate comment with marking at side of source code."
  :global t
  :group 'comment-annotate-mode
  :init-value nil
  (if comment-annotate-mode
      ;; TODO
      ))

是这个吗?

(save-restriction
            (narrow-to-region (region-beginning) (region-end))
            (deactivate-mark)
            (goto-char (point-min))
            (funcall fn c)
            (while (< (point) (point-max))
              (next-line)
              (when (< (point) (point-max))
                (funcall fn c))))

我试一下,看上去复合需要的逻辑。用了 narrow-to-region 也许是个不错的想法。

遇到个问题,那就是 narrorw-to-region 后, line-number-at-pos 得到的行号是在narrow里的,不是相对于整个完整buffer的行号。这个应该怎么解决?

(save-restriction
    (narrow-to-region (region-beginning) (region-end))
    (deactivate-mark)
    (goto-char (point-min))
    (while (< (point) (point-max))
      (next-line)
      (when (< (point) (point-max))
        (line-number-at-pos))))

有了, line-number-at-pos 有 ABSOLUTE 参数. 问题解决

实现的代码如下,可用。

(defvar comment-annotate--line-number-list nil
  "A list of line numbers of region selected text.")

(defun comment-annotate--line-numbers (start end)
  "Get a list of line numbers of region selected text."
  (setq comment-annotate--line-number-list nil)
  (save-excursion
    (when (region-active-p)
      (save-restriction
        (narrow-to-region (region-beginning) (region-end))
        (deactivate-mark)
        (goto-char (point-min))
        (while (< (point) (- (point-max) 1))
          (add-to-list 'comment-annotate--line-number-list (line-number-at-pos nil t))
          (next-line)))
      (setq comment-annotate--line-number-list (reverse comment-annotate--line-number-list))))
  comment-annotate--line-number-list)