Yevgnen / ivy-rich

More friendly interface for ivy.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Scrolling in switch to buffer causes extreme lag when ivy-rich-mode is on

konkrotte opened this issue · comments

This is unbearable and it can cause Emacs to freeze for seconds but when I turn ivy-rich-mode off there are no lags. I am using Emacs 28.0.5 (HEAD) if that helps. Scrolling in M-x also causes lags but not nearly as severe.

Same here on Windows 10, Emacs 27 with Doom Emacs. ivy-rich on 1097013

Its really slow when you move selection in any buffer switching buffer, like +ivy/switch-workspace-buffer.

For me navigating M-x is fine.

Holding down arrow key in buffer list (~50 buffers):

- command-execute                                                 807  94%
 - call-interactively                                             807  94%
  - funcall-interactively                                         807  94%
   - +ivy/switch-workspace-buffer                                 807  94%
    - +ivy--switch-buffer                                         807  94%
     - let                                                        807  94%
      - ivy-read                                                  807  94%
       - read-from-minibuffer                                     805  94%
        - ivy--queue-exhibit                                      799  93%
         - ivy--exhibit                                           799  93%
          - ivy--update-minibuffer                                788  92%
           - ivy--format                                          788  92%
            - mapcar                                              785  91%
             - ivy-rich--ivy-switch-buffer-transformer            785  91%
              - ivy-rich-format                                   785  91%
               - mapconcat                                        785  91%
                - #<compiled 0x1857235>                           785  91%
                 - ivy-rich-format-column                         785  91%
                  - ivy-rich-switch-buffer-path                   397  46%
                   - ivy-rich--switch-buffer-root-and-filename    397  46%
                    - ivy-rich-switch-buffer-root                 397  46%
                     - projectile-project-root                    397  46%
                      - cl-some                                   396  46%
                       - #<compiled 0x49258c5>                    396  46%
                        + file-truename                           255  29%
                        + projectile-root-top-down                 75   8%
                        + projectile-root-bottom-up                42   4%
                        + projectile-root-top-down-recurring       21   2%
                  - ivy-rich-switch-buffer-project                366  42%
                   - ivy-rich-switch-buffer-root                  366  42%
                    - projectile-project-root                     364  42%
                     - cl-some                                    364  42%
                      - #<compiled 0x4922b6d>                     364  42%
                       + file-truename                            237  27%
                       + projectile-root-top-down                  55   6%
                       + projectile-root-bottom-up                 53   6%
                       + projectile-root-top-down-recurring        16   1%
                      file-remote-p                                 1   0%
                    + ivy-rich--switch-buffer-directory             1   0%
                  + +ivy-rich-buffer-icon                          12   1%
                  + ivy-rich-switch-buffer-indicators               2   0%
                    ivy-switch-buffer-transformer                   1   0%
            + ivy--wnd-cands-to-str                                 3   0%
          + ivy--insert-minibuffer                                 11   1%
+ ...                                                              41   4%
+ timer-event-handler                                               7   0%

EDIT: Even cycling over as few as 5 buffers introduce serious lag.

This looks familiar: doomemacs/doomemacs#1317

I am also experiencing the same lag. Based on your profiler output I think projectile-project-root is slow, but ivy-rich-switch-buffer-project overall is also slow (and calls projectile-project-root multiple times). In any case, I solved this by creating a cache for the transformations (I think this method should be adopted by ivy-rich).

(defvar ivy-rich--ivy-switch-buffer-cache
  (make-hash-table :test 'equal))

(define-advice ivy-rich--ivy-switch-buffer-transformer
    (:around (old-fn x) cache)
  (let ((ret (gethash x ivy-rich--ivy-switch-buffer-cache)))
    (unless ret
      (setq ret (funcall old-fn x))
      (puthash x ret ivy-rich--ivy-switch-buffer-cache))
    ret))

(define-advice +ivy/switch-buffer
    (:before (&rest _) ivy-rich-reset-cache)
  (clrhash ivy-rich--ivy-switch-buffer-cache))

It is clearly still slow when a new candidate is shown, but it's better than nothing.

To make things even more seemless and also faster the first time switch-buffer is called I added a timer to re-build the cache in the background when idle:

(eval-after-load 'ivy-rich
  (progn
    (defvar ek/ivy-rich-cache
      (make-hash-table :test 'equal))

    (defun ek/ivy-rich-cache-lookup (delegate candidate)
      (let ((result (gethash candidate ek/ivy-rich-cache)))
        (unless result
          (setq result (funcall delegate candidate))
          (puthash candidate result ek/ivy-rich-cache))
        result))

    (defun ek/ivy-rich-cache-reset ()
      (clrhash ek/ivy-rich-cache))

    (defun ek/ivy-rich-cache-rebuild ()
      (mapc (lambda (buffer)
              (ivy-rich--ivy-switch-buffer-transformer (buffer-name buffer)))
            (buffer-list)))

    (defun ek/ivy-rich-cache-rebuild-trigger ()
      (ek/ivy-rich-cache-reset)
      (run-with-idle-timer 1 nil 'ek/ivy-rich-cache-rebuild))

    (advice-add 'ivy-rich--ivy-switch-buffer-transformer :around 'ek/ivy-rich-cache-lookup)
    (advice-add 'ivy-switch-buffer :after 'ek/ivy-rich-cache-rebuild-trigger)))

Apologies for the noob question, but should I include both @haji-ali and @ErkiDerLoony's code in my .config file (I'm running Doom), or just @ErkiDerLoony's. Thanks.

@ErkiDerLoony’s code is enough (and more sophisticated).