elisp 在修改选中区域时,会自动修改选中的区域范围吗?

今天尝试用 elisp 写一个给每行文本前添加两个全宽空格的函数

(defun add-two-ideographic-spaces-at-bol ()
  "Add two ideographic spaces at non-empty line beginning."
  (interactive)
  (let (start end)
    (if (use-region-p)
        (setq start (region-beginning)
              end (region-end))
      (setq start (point-min)
            end (point-max)))
    (save-excursion
      (goto-char start)
      (while (< (point) end)
        (message "BEFORE point: %s, start: %s, end: %s" (point) start end)
        (back-to-indentation)
        (delete-space--internal " \t ​" nil)
        ;; Insert ideographic space at non-blank line only.
        (unless (= (pos-bol) (pos-eol))
          (insert-char #x3000 2))
        ;; Update `end' after insert spaces.
        (setq end (if (use-region-p) (region-end) (point-max)))
        (message "AFTER point: %s, start: %s, end: %s" (point) start end)
        (forward-line)))))

在选中区域调用这个函数时,可以看到最开始返回的 start 和 end 都是自己选中的区域,插入两个空格后,start 不变,end 变成了插入的两个空格之后,也就是 3

给我的感觉是,调用 insert 之后,最开始选中的区域变成了 insert 操作的区域,所以这时 region-end 返回的值就是 (point) 所在的位置。

但如果操作完后不更新 end,当选中了多行时,在添加完所有行空格后,最后 end 的值会大于最开始设置的 end 值,这时 (point) 也会大于 end,就导致最后几行不会加上空格。

并且最神奇的是,在选择区域的情况下,如果我是从上往下选择,只有第一行文本的开头会加上空格,也就是能稳定复现我上面说的情况;如果是从下往上选择,就可以正常工作 :joy:

是我写的函数有什么问题吗?大家可以随便找些文本测试下,这是我的测试文本:

过圣诞苹果机坝隧道巴士
12395e5
abcd
对古巴迫使度过
ksdgasd
sdbgopasdjg
sadgbasdihjgbas
gbasidfopugb

相比于手动更新 end,更好的方法或许是使用marker

(defun add-two-ideographic-spaces-at-bol ()
  "Add two ideographic spaces at non-empty line beginning."
  (interactive)
  (let (start end)
    (if (use-region-p)
        (setq start (copy-marker  (region-beginning))
              end (copy-marker (region-end)))
      (setq start (copy-marker (point-min))
            end (copy-marker  (point-max))))
    (save-excursion
      (goto-char start)
      (while (< (point) end)
        (message "BEFORE point: %s, start: %s, end: %s" (point) start end)
        (back-to-indentation)
        (delete-space--internal " \t ​" nil)
        ;; Insert ideographic space at non-blank line only.
        (unless (= (pos-bol) (pos-eol))
          (insert-char #x3000 2))
        ;; Update `end' after insert spaces.
        ;; (setq end (if (use-region-p) (region-end) (point-max)))
        (message "AFTER point: %s, start: %s, end: %s" (point) start end)
        (forward-line)))))

1 个赞

牛啊大佬,今天又学到了