vertico-posframe, 喜欢的同学拿去

我发现 vertico-posframe 的实现方式细节处理特别麻烦,不知道 mini-frame 的实现方式稳定性如何。

selectrum + mini-frame + posframe 方案,折腾一番,水平有限,只能做到下图这样的,离company-mode/ company-posframe还差一些。(注:模拟成company是最终目的。)

1、灰色为输入框,由mini-frame提供。不足之处:mini-frame的miniuffer-prompt不知道怎么去掉了,这两函数好像是可以编辑输入框的格式的,自己瞎搞没效果;其宽度设置方式(width . 0.3)与posframe的 :width 不同,不知道怎么弄成一样宽。

2、绿色为结果框,由posframe提供。不足之处:用 company-posframe-poshandler 作为selectrum + posframe的poshandler,会出错:Error in post-command-hook (selectrum--update): (wrong-type-argument number-or-marker-p nil)

这个方案的配置其实就是 Additional Configuration · radian-software/selectrum Wiki · GitHub + Additional Configuration · radian-software/selectrum Wiki · GitHub 叠加起来恰好实现了“输入框和结果框分离”,后面调好两者child-frame的样式和弹出位置即可:

稳定性应该没问题。

看了看mini-frame的代码,它是通过read-frome-minibuffer来获取minibuffer的内容,效果很彻底,连C-x r t这样的提示都获取到。

另外再通过“make-fame”来生成child-frame,child-frame显示的过程比较流畅,像是从poshandler这里往下拉开的。而posframe会有一个对齐的过程,例如:posframe-poshandler-p0p0-to-p0p1会看得到child-frame先在p0p0生成,再对齐到p0p1。这个过程虽然一闪而过,但肉眼也能看得出。可能与系统和emacs版本有关,我的是macos和emacs-plus 29。

大佬可以用下面的配置简单测试一下:

(use-package mini-frame
  :hook (window-setup . mini-frame-mode))

(use-package posframe
  :hook (window-setup . (lambda () (require 'posframe))))

(use-package selectrum
  :hook (window-setup . selectrum-mode)
  :config
  (setq mini-frame-show-parameters                                        
      (lambda ()                                                                
        (let* ((info (posframe-poshandler-argbuilder))
               (posn (posframe-poshandler-point-bottom-left-corner info))
               (left (car posn))
               (top (cdr posn)))
          `((left . ,left)
            (top . ,top))))))

题外话插一句 use-package hook 后面如果接 lambda 的话是不会 defer 的,也即直接 require

:hook (window-setup . (lambda () (require 'posframe)))这个展开不是 (add-hook 'window-setup-hook (lambda () (require 'posframe)))吗?我以为可以defer的呢,才疏学浅 :rofl:

就是这个展开近似等价于

(add-hook 'window-setup-hook (lambda () (require 'posframe)))
(require 'posframe)

需要手动给 use-package defer (别的非 lambda 的 hook 不需要)

(use-package posframe
  :defer t
  :hook (window-setup . (lambda () (require 'posframe))))

已纠正写法,谢谢 @Voleking 大佬~

楼歪了,让我扶正过来。目前已实现结果框的边缘大小与company-mode的margin大小一致,即一个字符宽度;两者与高亮行字符对齐一致:

  (setq selectrum-display-action '(selectrum-posframe))
  
  (defcustom selectrum-posframe-fringe 1
    "The width of selectrum-posframe-fringe."
    :type 'number)

  (defun posframe-poshandler-point-bottom-left-corner-fringe (info)
    "Poshandler looks like company-posframe."
    (let* ((parent-window (plist-get info :parent-window))
           (point (with-current-buffer (window-buffer parent-window)
                    (max (line-beginning-position)
			 (- (plist-get info :position) selectrum-posframe-fringe))))
           (info (plist-put info :position-info (posn-at-point point parent-window))))
      (posframe-poshandler-point-bottom-left-corner info)))

  (defun selectrum-posframe (buffer _alist)
    "Function used by `selectrum-display-action."
    (frame-root-window
     (posframe-show buffer
		    :min-height 10
                    :min-width 30
		    :background-color "#b4eeb4" ;; hl-line background color
                    :left-fringe (* (frame-char-width) selectrum-posframe-fringe)
                    :right-fringe (* (frame-char-width) selectrum-posframe-fringe)
                    :poshandler 'posframe-poshandler-point-bottom-left-corner-fringe
		    :lines-truncate t
		    )))
  (set-face-attribute 'fringe nil :background 'unspecified :inherit 'hl-line)
  (add-hook 'minibuffer-exit-hook 'posframe-delete-all)

TODO:

1、隐藏 “25 M-x”,即 candidates count 和 minibuffer-prompt ;

2、调整“结果框posframe”的宽高使之随candidaes长短多少而变化;

3、fringe背景色与“select-candidate”背景色不一致,可以考虑去掉finge,取而代之的是给每个candidate双侧加上一个字符宽度。

非IT专业,搞不动了……希望各位大佬出手相助 :pray: :pray: :pray:

Emacs 29 minibuff这里会显示文字并且会挡住 modeline,不管是 doom-modeline 还是默认的 modeline

看来是minibuffer遮挡器的位置不对。

ivy-posframe 遮挡也有问题,C-g 之后的 [Quit] 就遮挡不了。

这个我知道,但这是使用overlay遮挡存在的问题,不好处理,所以我才试验posframe遮挡器,但有时候遮挡的太严实 :sweat_smile:

多试验下,也许就找到最佳方案了 :grin:

公司电脑的 emacs-plus©28 遮挡好像没问题,家里的是 emacsmacport

@tumashu 大佬,请教一下,为啥posframe的 :width:height 不能生效:(下面的代码是在这基础上改的:Additional Configuration · radian-software/selectrum Wiki · GitHub

   (defun selectrum-posframe (buffer _alist)
    "Function used by `selectrum-display-action."
    (frame-root-window
     (posframe-show buffer
		    :min-width 30
		    :width 60
		    :min-height 5
		    :height 10
		    :background-color "#b4eeb4" ;; hl-line background color
		    :left-fringe (min 0 (* (frame-char-width) selectrum-posframe-fringe))
		    :right-fringe (min 0 (* (frame-char-width) selectrum-posframe-fringe))
		    :poshandler 'posframe-poshandler-point-bottom-left-corner-fringe
		    :lines-truncate t
		    )))

通过:override-parameters传参数也不行,只有 min-widthmin-height才能生效。另外,传参数(min-height . 2)不能 override 掉:min-height 5,只能起到“补充参数”的作用,例如,没有:min-height 5,则(min-height . 2)生效。

  (defcustom selectrum-posframe-parameters nil
    "The frame parameters used by selectrum-posframe."
    :type 'string)

  (setq selectrum-posframe-parameters
	'(
	  (min-width . 30)
	  (min-height . 2)
	  (width . 60)
	  (height . 10)
	  ))

  (defun selectrum-posframe (buffer _alist)
    "Function used by `selectrum-display-action."
    (frame-root-window
     (posframe-show buffer
		    :min-width 30
		    :width 60
		    :min-height 5
		    :height 10
		    :background-color "#b4eeb4" ;; hl-line background color
		    :left-fringe (min 0 (* (frame-char-width) selectrum-posframe-fringe))
		    :right-fringe (min 0 (* (frame-char-width) selectrum-posframe-fringe))
		    :poshandler 'posframe-poshandler-point-bottom-left-corner-fringe
		    :override-parameters selectrum-posframe-parameters
		    :lines-truncate t
		    )))

如图,设置height为10,实际只有min-height的5;整个posframe只要有min-width和min-height大小,width和height无效。

尝试用了posframe readme的最简单的使用示例,和更换emacs版本为nightly,都有这个不能指定width和height的问题。

主要目的是实现posframe随candidate长短多少而变化宽高。在折腾过程在就发现了上述问题。

刚简单测试了一下,这个“一闪而过”的过程应该是用了 posframe-delete,下一次激活posfram则需要要在emacs左上角生成再对齐(闪)到新的位置。而使用 posframe-hide则仅仅隐藏posframe,下一次激活posframe时立即在新的位置显示出来,没有“一闪而过”这个过程。

这也能解释下面这个问题了:

对,不知道能不能解决这个一闪而过的问题

那估计需要把XXX-posframe源代码的posframe-delete替换为posframe-hide:thinking:

第一次还是会有吧? 让 @tumashu 大佬 看看怎么弄

试试最新代码,原来的这两个参数指的是 max-width 和 max-height

有些 posframe-show 参数 不是 frame parameters, 所以也就不起作用。