minad / corfu

:desert_island: corfu.el - COmpletion in Region FUnction
GNU General Public License v3.0
1.15k stars 43 forks source link

popupinfo visual flashing on MacOS due to brief split-window #268

Closed jdtsmith closed 1 year ago

jdtsmith commented 1 year ago

popupinfo seems like a great addition, and solves the problem of too many chefs working in the echo area for docstrings/flymake/lsp info etc. On MacOS there is a noticeable visual flicker when each popupinfo frame is displayed, with or without hiding in between. What appears in the "flash" is the text of the current buffer, shifted by half a page or so (including the modeline from below it), before disappearing. To me it looks like the current window is being split, a new window visiting the same buffer is created, and then swiftly buried just as the child frame comes into view. Interestingly, if the frame already has two windows, the flashing does not occur. And the flashing is directional: if you've last split the window with C-x 3, it happens in a phantom horizontally aligned disappearing window.

https://user-images.githubusercontent.com/93749/205507594-6da4778e-2099-407f-8655-6e5d8749a3ab.mov

By tracing, I found split-window is called indirectly by elisp-mode capf's company doc function:

1 -> (split-window nil nil t)  backtrace-to-string(nil)
  (closure (t) nil (backtrace-to-string (backtrace-get-frames 'backtrace)))()
  #f(compiled-function (body &rest args) #<bytecode 0x10b4b1ca74e1308e>)(#f(advice-wrapper :after #<subr split-window> moody-redisplay) nil nil t)
  apply(#f(compiled-function (body &rest args) #<bytecode 0x10b4b1ca74e1308e>) #f(advice-wrapper :after #<subr split-window> moody-redisplay) (nil nil t))
  split-window(nil nil t)
  split-window-right()
  split-window-sensibly(#<window 3 on eglot_didchange.el>)
  window--try-to-split-window(#<window 3 on eglot_didchange.el> ((inhibit-switch-frame . t)))
  display-buffer-pop-up-window(#<buffer *Help*> ((inhibit-switch-frame . t)))
  display-buffer--maybe-pop-up-window(#<buffer *Help*> ((inhibit-switch-frame . t)))
  display-buffer--maybe-pop-up-frame-or-window(#<buffer *Help*> ((inhibit-switch-frame . t)))
  display-buffer(#<buffer *Help*> nil)
  temp-buffer-window-show(#<buffer *Help*> nil)
  describe-function(defface)
  elisp--company-doc-buffer("defface")
  apply(elisp--company-doc-buffer "defface" nil)
  #f(compiled-function (cand &rest args) #<bytecode 0x1ff418cad9865c6>)("defface")
  corfu-popupinfo--get-documentation("defface")
  funcall(corfu-popupinfo--get-documentation "defface")
  (and t (funcall corfu-popupinfo--function candidate))
  (let* ((content (and t (funcall corfu-popupinfo--function candidate)))) (if content (save-current-buffer (set-buffer (corfu--make-buffer " *corfu-popupinfo*")) (let* ((modified (buffer-modified-p)) (buffer-undo-list t) (inhibit-read-only t) (inhibit-modification-hooks t)) (unwind-protect (progn (erase-buffer) (insert content) (goto-char (point-min))) (if modified nil (restore-buffer-modified-p nil)))) (let ((--dolist-tail-- corfu-popupinfo--buffer-parameters)) (while --dolist-tail-- (let ((var ...)) (set (make-local-variable ...) (cdr var)) (setq --dolist-tail-- (cdr --dolist-tail--))))) (progn (setq face-remapping-alist (copy-tree face-remapping-alist)) (let* ((p (if ... ... ...))) (progn (if p (setcdr p ...) (setq face-remapping-alist ...)) 'corfu-popupinfo)))) (progn (if (eq corfu-popupinfo--toggle 'init) nil (message "No %s available for `%s'" (car (last (split-string ... "-+"))) candidate)) (corfu-popupinfo--hide) (setq cand-changed nil coords-changed nil))))
  (progn (let* ((content (and t (funcall corfu-popupinfo--function candidate)))) (if content (save-current-buffer (set-buffer (corfu--make-buffer " *corfu-popupinfo*")) (let* ((modified (buffer-modified-p)) (buffer-undo-list t) (inhibit-read-only t) (inhibit-modification-hooks t)) (unwind-protect (progn (erase-buffer) (insert content) (goto-char ...)) (if modified nil (restore-buffer-modified-p nil)))) (let ((--dolist-tail-- corfu-popupinfo--buffer-parameters)) (while --dolist-tail-- (let (...) (set ... ...) (setq --dolist-tail-- ...)))) (progn (setq face-remapping-alist (copy-tree face-remapping-alist)) (let* ((p ...)) (progn (if p ... ...) 'corfu-popupinfo)))) (progn (if (eq corfu-popupinfo--toggle 'init) nil (message "No %s available for `%s'" (car (last ...)) candidate)) (corfu-popupinfo--hide) (setq cand-changed nil coords-changed nil)))))
  (if cand-changed (progn (let* ((content (and t (funcall corfu-popupinfo--function candidate)))) (if content (save-current-buffer (set-buffer (corfu--make-buffer " *corfu-popupinfo*")) (let* ((modified ...) (buffer-undo-list t) (inhibit-read-only t) (inhibit-modification-hooks t)) (unwind-protect (progn ... ... ...) (if modified nil ...))) (let ((--dolist-tail-- corfu-popupinfo--buffer-parameters)) (while --dolist-tail-- (let ... ... ...))) (progn (setq face-remapping-alist (copy-tree face-remapping-alist)) (let* (...) (progn ... ...)))) (progn (if (eq corfu-popupinfo--toggle 'init) nil (message "No %s available for `%s'" (car ...) candidate)) (corfu-popupinfo--hide) (setq cand-changed nil coords-changed nil))))))
  (let* ((cand-changed (not (and (corfu-popupinfo--visible-p) (equal candidate corfu-popupinfo--candidate)))) (new-coords (frame-edges corfu--frame 'inner-edges)) (coords-changed (not (equal new-coords corfu-popupinfo--coordinates)))) (if cand-changed (progn (let* ((content (and t (funcall corfu-popupinfo--function candidate)))) (if content (save-current-buffer (set-buffer (corfu--make-buffer " *corfu-popupinfo*")) (let* (... ... ... ...) (unwind-protect ... ...)) (let (...) (while --dolist-tail-- ...)) (progn (setq face-remapping-alist ...) (let* ... ...))) (progn (if (eq corfu-popupinfo--toggle ...) nil (message "No %s available for `%s'" ... candidate)) (corfu-popupinfo--hide) (setq cand-changed nil coords-changed nil)))))) (if (or cand-changed coords-changed) (progn (let* ((border (alist-get 'child-frame-border-width corfu--frame-parameters)) (val (corfu-popupinfo--area (if cand-changed ... ...)))) (progn (ignore (consp val)) (let* ((x20 ...) (x21 ...)) (progn (ignore ...) (let* ... ...))))))))
  (progn (let* ((cand-changed (not (and (corfu-popupinfo--visible-p) (equal candidate corfu-popupinfo--candidate)))) (new-coords (frame-edges corfu--frame 'inner-edges)) (coords-changed (not (equal new-coords corfu-popupinfo--coordinates)))) (if cand-changed (progn (let* ((content (and t ...))) (if content (save-current-buffer (set-buffer ...) (let* ... ...) (let ... ...) (progn ... ...)) (progn (if ... nil ...) (corfu-popupinfo--hide) (setq cand-changed nil coords-changed nil)))))) (if (or cand-changed coords-changed) (progn (let* ((border (alist-get ... corfu--frame-parameters)) (val (corfu-popupinfo--area ...))) (progn (ignore (consp val)) (let* (... ...) (progn ... ...))))))))
  (if (and (corfu-popupinfo--visible-p corfu--frame)) (progn (let* ((cand-changed (not (and (corfu-popupinfo--visible-p) (equal candidate corfu-popupinfo--candidate)))) (new-coords (frame-edges corfu--frame 'inner-edges)) (coords-changed (not (equal new-coords corfu-popupinfo--coordinates)))) (if cand-changed (progn (let* ((content ...)) (if content (save-current-buffer ... ... ... ...) (progn ... ... ...))))) (if (or cand-changed coords-changed) (progn (let* ((border ...) (val ...)) (progn (ignore ...) (let* ... ...))))))))
  corfu-popupinfo--show("defface")
  corfu-popupinfo--exhibit(auto)
  apply(corfu-popupinfo--exhibit auto)
  corfu--exhibit(auto)
  corfu--auto-complete-deferred((#<buffer eglot_didchange.el> 3492 1801))
  apply(corfu--auto-complete-deferred (#<buffer eglot_didchange.el> 3492 1801))
  timer-event-handler([t 25484 58376 703646 nil corfu--auto-complete-deferred ((#<buffer eglot_didchange.el> 3492 1801)) nil 999999 nil])

And here is a telling comment:

(defun elisp--company-doc-buffer (str)
  (let ((symbol (intern-soft str)))
    ;; FIXME: we really don't want to "display-buffer and then undo it".
jdtsmith commented 1 year ago

Proposed fix in #269.