有哪些阻止 start-process 滚动 output buffer 的方法?

默认情况下,输出缓冲会一直滚动到底部:

Code
(let ((buffer (generate-new-buffer " *test*"))
      proc)

  (setq proc (start-process "Shell" buffer "/bin/bash" "-c"
                            "for i in {1..100}; do echo \"Line $i\"; done"))

  (pop-to-buffer buffer))

方法1,最简单也是最"脏",在输出缓冲插入至少一个字符:

Code
(let ((buffer (generate-new-buffer " *test*"))
      proc)

  (with-current-buffer buffer
    (insert "\n")
    (goto-char (point-min)))

  (setq proc (start-process "Shell" buffer "/bin/bash" "-c"
                            "for i in {1..100}; do echo \"Line $i\"; done"))

  (pop-to-buffer buffer))

脏不单指视觉,即使插入一个不可见字符,输出内容也着实被污染了,除非最后再加一个删除的步骤,但这又让事情变复杂了。

方法2,自定义 filter:

Code
(let ((buffer (generate-new-buffer " *test*"))
      proc)

  (setq proc (start-process "Shell" buffer "/bin/bash" "-c"
                            "for i in {1..100}; do echo \"Line $i\"; done"))

  (set-process-filter
   proc (lambda (proc string)
          (when (buffer-live-p (process-buffer proc))
            (with-current-buffer (process-buffer proc)
              (save-excursion
                (goto-char (point-max))
                (insert string)
                (set-marker (process-mark proc) (point)))))))

  (pop-to-buffer buffer))

替换原生的 filter 就为了移动光标位置,多少感觉有点小题大作。

不知道还有没有其他更简单的方法?

1 个赞

直接找到start-process滚动buffer的地方,写个advice?

创建异步进程时,进程的 process-mark 默认位于 buffer 的 (point-max) 位置,这也就是说如果 buffer 为空的话 (point)process-mark 会重叠,根据文档来看,当两者重叠时就会在新内容到达时移动光标。

对非空 buffer,直接在创建进程后将光标移动到 buffer 开头即可,对空 buffer,可以考虑在 buffer 中有内容到达后将光标移至 buffer 开头。我的思路是用 timer 检查 buffer 是否为空,然后将光标移动到 buffer 开头,从而取消掉光标随 process-mark 的移动:

(defun my-move-to-top (process)
  (when (buffer-live-p (process-buffer process))
    (let (it)
      (setq it (lambda ()
		 (when (buffer-live-p (process-buffer process))
		   (if (= 0 (buffer-size (process-buffer process)))
		       (run-at-time 0.1 nil it)
		     (with-current-buffer (process-buffer process)
		        (goto-char (point-min)))))))
      (run-at-time 0.1 nil it))))

start-process 的返回值作为该函数的参数即可。当条件满足时 timer 回调就会执行。