关于这件事还得从这几天试用 company-box 遇到的他的tooltip的 childframe绘制的问题开始谈起。
之前在18年的时候我试用过company-box,但是因为当时它的性能问题而放弃了, 又因为之前一直在使用26版本的emacs,新版的company-box在其之上依然很卡, 遂搁置了很久。
由于种草于centaur-emacs很久了,想探索一些新的包或者有一些配置上的 实现需要借鉴的,我都会到它那里瞟一下。虽然很久就看到centuar-emacs引用 了company-box这个包,并且是默认在gui session中开启的,而且它除了对icon 映射的一些自定义外并没有什么性能上的patch(当然除了限制了一下 candidates的长度),于是乎我就很疑惑了,不知道大佬是怎么在company-box 的lag中愉快的玩耍的,难道颜值既是正义?
最近想对自己的配置做了pdumper的调优,于是把自己的emacs版本升级到了 emacs-27分支上, 使用27.0.91的tag。并且在手闲之际,再度试用了一下2020年 的company-box更新,虽然还是有点卡,但是相比之前可以说可以接受了(之后 在26.3上又试用了一下,依然卡,看来是emacs 27版本的某些优化吧),但是发 现一个问题:
在candi列表的滚动中,随着candidtes的更新,tooltip的位置和大小虽然也 会做相应的更新,但是母frame中会有它之前的残影,也就是之前的tooltip布 局还在,于是乎就会出现这么一种情况既candi列表的滚动怎么也不会到底, 这其实一种错觉,因为如果心得candi列表比之前的短的话,那么新绘制的 tooltip的高度当然比之前要短,如果之前的布局还在的话,两者一重叠,那 么就很会出现这种情况。而且在tooltip右边的滚动条也会随着当前candi列表 的最大宽度的变化而留残影,至此我想这两者是同一个问题。
于是乎我怀疑是company-box的tooltip的size和position更新机制的bug,于是
看了一下源码,发现它是使用childframe来绘制candi列表的,并且更新很自然
的就是对childframe的parameter做调整和使用set-frame-position
来做位置
信息更新,常规操作,没有错误啊,于是我陷入了苦思,很显然我开始怀疑是不
是之前自己的配置中的某些advice导致的?或者是使用的众多包中有什么不兼容?
于是乎用vanilla配置试了一下,同样,那么这个,我开始怀疑是emacs-devel的
问题,于是看了一下commit log,发现了在c49d379f17bcb0
中有对childframe
的相关更新,发现了一个选项x-gtk-resize-child-frames
, 详情如下:
If non-nil, resize child frames specially with GTK builds. If this is nil, resize child frames like any other frames. This is the default and usually works with most desktops. Some desktop environments (GNOME shell in particular when using the mutter window manager), however, may refuse to resize a child frame when Emacs is built with GTK3. For those environments, the two settings below are provided.
If this equals the symbol ’hide’, Emacs temporarily hides the child frame during resizing. This approach seems to work reliably, may however induce some flicker when the frame is made visible again.
If this equals the symbol ’resize-mode’, Emacs uses GTK’s resize mode to always trigger an immediate resize of the child frame. This method is deprecated by GTK and may not work in future versions of that toolkit. It also may freeze Emacs when used with other desktop environments. It avoids, however, the unpleasent flicker induced by the hiding approach.
This variable is considered a temporary workaround and will be hopefully eliminated in future versions of Emacs.
这才想到自己使用的是用gtk编译linux上的emacs,那么对应的这个选项中的
hide
值就是我可以一试的地方。果然,改善了,虽然就像之中说的有闪烁的
问题,在make invisible到重新创建childframe的过程中。
所以根据这条提示,我有两种方案可以不完美解决这个问题:
-
很简单直接
(setq x-gtk-resize-child-frames 'hide)
,但是这种方法在 其描述中有提到这个变量是个暂时的解决方案,并且在未来的正式版中可能 会被移除,因此加上boundp
的判断是必须的。 -
这第二种方案是在第一种方案中提到的暂时变量不可用的情况下,并且上游 仍然没有解决这个问题的情况下使用的,就是咋每次窗口高度更新的时候强 制先make invisible它,等相关参数更新完成后在打开它:
(defvar my--company-box-company-prefix nil)
(defvar my--company-box-company-candidates-length nil)
(defun company-box-frontend (command)
"`company-mode' frontend using child-frame.
COMMAND: See `company-frontends'.
NOTE: this function has been redefined for temporal bug fake fix
due to the emacs child-frame bug."
(unless (stringp my--company-box-company-prefix)
(setq my--company-box-company-prefix company-prefix))
(unless my--company-box-company-candidates-length
(setq my--company-box-company-candidates-length
company-candidates-length))
(cond
((eq command 'hide)
(company-box-hide)
(setq my--company-box-company-prefix nil
my--company-box-company-candidates-length nil))
((and (equal company-candidates-length 1)
(null company-box-show-single-candidate))
(company-box-hide))
((eq command 'update)
(when (or
;;; Update according to prefix length compare
;; (and (or (string-prefix-p my--company-box-company-prefix
;; company-prefix)
;; (string-prefix-p company-prefix
;; my--company-box-company-prefix))
;; (> (abs (- (length company-prefix)
;; (length my--company-box-company-prefix)))
;; 3))
nil
(>= (abs (- my--company-box-company-candidates-length
company-candidates-length))
1))
(setq my--company-box-company-prefix company-prefix)
(setq my--company-box-company-candidates-length
company-candidates-length)
(company-box-hide))
(company-box-show))
((eq command 'post-command)
(company-box--post-command))))
第二种方案无法解决scrollbar重影的问题,因为scrollbar是通过side-window 来实现的,还需要写一个patch基于宽度改变关闭重开side-window的原理。因为 问题不大,就不管了,不然又是一笔性能开销。
至此,本帖结束,希望对其他也有此问题的朋友有所帮助,同时也希望大家分享
其他解决方案,另外如果有对emacs-devel熟悉的朋友,想向你了解下,emacs
gtk环境下的这个 x-gtk-resize-child-frames
选项的最终演进会是什么,以
及emacs27正式版中是否会对此给出最终解决方案?