Yevgnen/ivy-rich

ivy-rich-switch-buffer-root performance is pretty ugly

s-kostyaev opened this issue · 7 comments

Hi!
Thanks for this cool project.
Unfortunately I can't switch buffer with long buffer list if I use ivy-rich with default settings. I need to disable a couple of transformers. Before I did it, go to next line in ivy-switch-buffer can spend seconds.
This is screenshot of profiler-report:

Emacsa9l1Sr

How many buffers did you open? Did you open any remote buffer over tramp?

190 buffers are open. No one from tramp. Most buffers from 2 projects, and several from couple other projects.

That's quite a lot...

I tested in 300+ buffers with multiple projects (org notes, Python projects) and haven't encountered the issue. All of projectile, ffip and project.el work well. Are you using the master version of ivy-rich? Do you have complicate project structures? Does this happen with a small number of buffers opened?

Are you using the master version of ivy-rich?

I use 20201118.16 version

Does this happen with a small number of buffers opened?

No. Looks like it start to be as slow as many buffers with project.el git project (vc-project) are opened. I use only project.el for project management.

Do you have complicate project structures?

What project structures are complicated? I have many files deep nested in project directories. Is it complicated enough?

projectile has trouble dealing with complicate project structures but can be relieved by customizing projectile-project-root-files-functions. I might take some time to see what I can do with project.el.

I also have a noticeable (but not that big) delay with (just) 30 buffers, most of them in no project, when calling ivy-switch-buffer/switching lines. As I use ivy-switch-buffer very often, I want it to be fast and that delay did bug me.

I did some hacking on-top of ivy-rich-swich-buffer-root and added:

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

(defun ivy-rich-switch-buffer-root (candidate)
  (when-let* ((dir (ivy-rich--switch-buffer-directory candidate)))
    (let ((cached-value (gethash dir ivy-rich-switch-buffer-root-cache 'not-found)))
      (if (not (eq cached-value 'not-found)) cached-value
        (let ((value
               (unless (or (and (file-remote-p dir)
                                (not ivy-rich-parse-remote-buffer))
                           ;; Workaround for `browse-url-emacs' buffers , it changes
                           ;; `default-directory' to "http://" (#25)
                           (string-match "https?://" dir))
                 (cond ((bound-and-true-p projectile-mode)
                        (let ((project (or (ivy-rich--local-values
                                            candidate 'projectile-project-root)
                                           (projectile-project-root dir))))
                          (unless (string= project "-")
                            project)))
                       ((require 'find-file-in-project nil t)
                        (let ((default-directory dir))
                          (ffip-project-root)))
                       ((require 'project nil t)
                        (when-let ((project (project-current nil dir)))
                          (car (project-roots project))))))))
          (puthash dir value ivy-rich-switch-buffer-root-cache)
          value)))))

This works well for now and the switching buffers is fast again. I think caching these results is a reasonable thing to do, as a file switching projects shouldn't happen often, right?

I would be happy to make a PR for this, if you don't mind. But there are few things I would also do:

  • Add an ivy-rich-clear-project-cache function, to clear the cache. That way the user can clear the cache after re-naming a project etc.
  • Add an kill-buffer-hook which evicts the closed buffer's path from the cache.
  • Add an ivy-rich-useproject-cache option to enable/disable this caching in general.

If you have any more suggestions, please let me know.

I don't use ivy such as ivy-rich anymore. But this solution looks reasonable.