修复 copilot 和 popper 一起使用时的报错 + 允许 popper 根据规则决定是否选中弹出的窗口

Copilot 打开时会弹出一个 warning,如果你使用了 popper 管理这个 warning 窗口而且设置 popper-display-functionpopper-select-popup-at-bottom,那么在弹出 warning 时会导致 current-buffer 转移到 warning 窗口,进而导致后续操作和 hooks 中各种函数发生错误(因为他们仍然以为自己在原来的 buffer 中)。

鉴于这个问题可能会带来意想不到的错误,因此仅修改 copilot 的代码是不够的,最好从根源上解决这个问题。解决的办法是允许 popper 根据定义的规则自行选中弹出的窗口是否应该被选中。在研究了 popper 的代码后我发现实现这一点并不容易,并且作者也不希望在这个包中实现这个功能(为了正交性)。因此我用一个 hack 一些的手段实现了这个操作。

:init
  (setq +popper-reference-buffers-select ;; ...
           +popper-reference-buffer-no-select ;; ...
           popper-reference-buffers (append +popper-reference-buffers-select
                                           +popper-reference-buffer-no-select))

  ;; ...
  :config
  ;; HACK: do not select window in `+popper-reference-buffer-no-select'
  (defvar +popper-unpacked-vars '(popper--reference-names
                                  popper--reference-modes
                                  popper--reference-predicates
                                  popper--suppressed-names
                                  popper--suppressed-modes
                                  popper--suppressed-predicates))
  (defvar +popper-unpacked-vars-no-select '())

  (dolist (var +popper-unpacked-vars)
    (let ((var-name (intern (concat "+" (symbol-name var) "-no-select"))))
      (eval
       `(progn
          (defvar ,var-name nil)
          (push ',var-name +popper-unpacked-vars-no-select)))))
  (setq +popper-unpacked-vars-no-select (reverse +popper-unpacked-vars-no-select))

  (cl-progv `(popper-reference-buffers ,@+popper-unpacked-vars)
      (list +popper-reference-buffer-no-select)
    (popper--set-reference-vars)
    (dolist (var +popper-unpacked-vars)
      (eval `(setq ,(intern (concat "+" (symbol-name var) "-no-select")) ',(symbol-value var)))))

  (defun +popper-smart-popup-at-bottom (buffer &optional alist)
    (let ((window (popper-display-popup-at-bottom buffer alist)))
      (unless (cl-progv +popper-unpacked-vars
                  (mapcar #'symbol-value +popper-unpacked-vars-no-select)
                (popper-popup-p buffer))
        (select-window window))))
  (setq popper-display-function #'+popper-smart-popup-at-bottom)