为何 corfu 把候选项直接输出到 buffer 而不是弹出菜单?

发现 corfu 有一定概率(跟打字速度和 corfu 的 timer 间隔有关)会把补全项直接输出到 buffer,而不是弹出菜单。

但这个现象只存在于的 corfu + elpl 组合,其他如 corfu + eshell/python-shell/ielm… 等都没有这个问题,而 company + elpl 也正常。

打了一些 debug 信息,发现出问题的时候,似乎 completion-at-point 动作并没有执行完,不知道那一堆候选项是哪来的,是哪个函数把它插入到 prompt 后面的?整个交互过程戛然而止:

正常情况下是 completion-at-point 是可以执行完的:

搜了一下 corfu 的 issues 列表,没发现有其他人报告类似问题。

测试的最小配置:

;; ---------------------------------------------------------------------------
;; elpa

(require 'seq)

(setq user-emacs-directory
      (car (seq-filter
            #'file-exists-p
            (list (format "~/.emacs.d/%s/" emacs-version)
                  (format "~/.emacs.d/%s/" emacs-major-version)
                  "~/.emacs.d/"))))

(setq package-user-dir (concat user-emacs-directory "elpa/"))

(package-initialize)

;; ---------------------------------------------------------------------------
;;  config

(add-to-list 'initial-frame-alist '(fullscreen . maximized))
(add-to-list 'default-frame-alist '(fullscreen . fullheight))

(add-to-list 'load-path "~/.repos/emacs-elpl")
(require 'elpl)

(add-to-list 'load-path "~/repos/emacs-corfu")
(require 'corfu)
(setq corfu-auto t)
(global-corfu-mode)

(unless (display-graphic-p)
  (add-to-list 'load-path "~/repos/emacs-corfu-terminal")
  (require 'corfu-terminal)
  (corfu-terminal-mode +1))

(run-with-timer 0.1 nil
                (lambda ()
                  (elpl)
                  (view-echo-area-messages)))

原因找到了,是 corfu--auto-complete-deferred 函数中的这句引 (while-no-input (run-hook-wrapped ...)) 起的:

    (pcase (while-no-input ;; Interruptible capf query
             (run-hook-wrapped 'completion-at-point-functions #'corfu--capf-wrapper))

这个 while-no-input 原本应该是用来避免 capf 等待太久。

但是,当处于某种击键的频率时(多试几次),while-no-input 会提前中断, 不等 capf 返回结果,就进入下一轮等待。进入下一轮时,突然上一轮的 capf 又有了结果,但没有接收者,所以就直接撒落在光标位置了。

while-no-input 改成 progn 就可以:

-   (pcase (while-no-input ;; Interruptible capf query
+   (pcase (progn
             (run-hook-wrapped 'completion-at-point-functions #'corfu--capf-wrapper))

没有 while-no-input 我也不觉得比以前卡顿,或者有什么变化。

1 个赞

提了 issue#212

用代码模拟按键输入,复现率更高:

  1. M-x async-shell-command RET

  2. 执行以下命令:

    python -c '
    import time
    import keyboard
    CORFU_DELAY = 0.21
    keyboard.press_and_release("g")
    time.sleep(CORFU_DELAY)
    keyboard.press_and_release("l")
    time.sleep(CORFU_DELAY)
    keyboard.press_and_release("o")
    '