Emacs 24.4 的 line-move 函数在 noninteractive 模式下会跑偏

刚才不小心又踩到一个坑,在 noninteractive 模式下执行 (line-move 1) 竟然跑偏了:

f[o]o
  |
  /  (line-move 1)
 |
 v
[b]ar
qux

代码:

(defun test-line-move--dump-buffer (current-pos)
  (dotimes (i (buffer-size))
    (let* ((pos (1+ i))
           (msg (format "point: %2d, char: %s" pos (char-to-string (char-after pos)))))
      (message (concat msg (if (= pos current-pos) " <-- (point)"))))))

(with-temp-buffer
  (insert "fo0\nbar\nqux\n") 

  (goto-char 2)
  (message ">>> Initial:")
  (test-line-move--dump-buffer (point))

  (line-move 1)
  (message ">>> After (line-move 1):")
  (test-line-move--dump-buffer (point)))

不同版本的测试结果:

Emacs 24.4                              | Emacs 24.5+
----------------------------------------+------------------------------------
>>> Initial:                            | >>> Initial:
point:  1, char: f                      | point:  1, char: f
point:  2, char: o <-- (point)          | point:  2, char: o <-- (point)
point:  3, char: 0                      | point:  3, char: 0
point:  4, char:                        | point:  4, char:
                                        |
point:  5, char: b                      | point:  5, char: b
point:  6, char: a                      | point:  6, char: a
point:  7, char: r                      | point:  7, char: r
point:  8, char:                        | point:  8, char:
                                        |
point:  9, char: q                      | point:  9, char: q
point: 10, char: u                      | point: 10, char: u
point: 11, char: x                      | point: 11, char: x
point: 12, char:                        | point: 12, char:
                                        |
>>> After (line-move 1):                | >>> After (line-move 1):
point:  1, char: f                      | point:  1, char: f
point:  2, char: o                      | point:  2, char: o
point:  3, char: 0                      | point:  3, char: 0
point:  4, char:                        | point:  4, char:
                                        |
point:  5, char: b <-- (point)          | point:  5, char: b
point:  6, char: a                      | point:  6, char: a <-- (point)
point:  7, char: r                      | point:  7, char: r
point:  8, char:                        | point:  8, char:
                                        |
point:  9, char: q                      | point:  9, char: q
point: 10, char: u                      | point: 10, char: u
point: 11, char: x                      | point: 11, char: x
point: 12, char:                        | point: 12, char:

解决之道就是给 line-move 写个 advice 把位置纠正过来。正常模式不存在这个问题,所以 advice 只是为了保证 ert 测试顺利进行,不影响正常使用。

1 个赞

貌似 24.3 也有问题。这样一段代码:

(with-temp-buffer
  (insert "ABC\nDEF")
  (goto-char 2)
  (line-move 1)
  (string (char-after)))

24.3 和 24.4 在 Batch Mode 下有问题,之后版本没问题:

~ $ emacs-24.3 --batch --eval '(progn (insert "ABC\nDEF") (goto-char 2) (line-move 1) (princ (string (char-after))))'
D
~ $ emacs-24.4 --batch --eval '(progn (insert "ABC\nDEF") (goto-char 2) (line-move 1) (princ (string (char-after))))'
D
~ $ emacs-24.5 --batch --eval '(progn (insert "ABC\nDEF") (goto-char 2) (line-move 1) (princ (string (char-after))))'
E
~ $ emacs-25.1 --batch --eval '(progn (insert "ABC\nDEF") (goto-char 2) (line-move 1) (princ (string (char-after))))'
E
~ $ emacs-25.3 --batch --eval '(progn (insert "ABC\nDEF") (goto-char 2) (line-move 1) (princ (string (char-after))))'
E
~ $ emacs-26.1 --batch --eval '(progn (insert "ABC\nDEF") (goto-char 2) (line-move 1) (princ (string (char-after))))'
E

(buffer-size) 更直接。

1 个赞

line-move 的实现大有问题。修复 24.4 跑偏的问题之后,24.5 又来添乱了。

昨天写了一堆 ert 测试,从 24.4 到 27.0,唯独 24.5 报 end-of-buffer 的错误,然而根本没有移动到底部。运行单个用例却又通过。

把出错那一行前面的调试信息 (message ...) 删掉之后就正常了,或者再加一句 (message ...) 错误也消失,完全摸不到规律。

只能离这个函数远点了。