Improve counsel-projectile-find-file performance

Current code will show significant lag for projects with large numbers
of files.  Tests with 400K files shows 15-20s lag for
counsel-projectile-find-file.  Profiling showed that this was
due to the preprocessing of projectile-current-project-files to add
'ivy-virtual face to files not already in a buffer.  To fix this,
a display transformer was added for this command so that the face
modification was applied only to at most ivy-height files.

The preprocessed file list was also used by counsel-projectile
code paths, so some additional modifications were made to ensure
that that code path does not change behavior.  Any similar issues in
counsel-projectile performance was not addressed in this commit.

Details:

* move 'ivy-virtual face change to a display transformer function
* use projectile-current-project-files instead of counsel-projectile--file-list
  for ivy-read.
* reduce the scope of counsel-projectile--file-list, making it return
  only unvisited files, and renaming it accordingly.  This is now only
  used indirectly by the counsel-projectile function.
* change ivy-switch-buffer-transformer to propertize files at display time.
This commit is contained in:
ambihelical 2017-07-15 15:15:09 -07:00 committed by Eric Danan
parent 9e3db12005
commit 2a46d758f7

View file

@ -65,20 +65,16 @@
;;; counsel-projectile-find-file
(defun counsel-projectile--file-list (&optional no-buffer)
"Return a list of files for the current project.
(defun counsel-projectile--unvisited-file-list ()
"Return a list of unvisited files for the current project.
Like `projectile-current-project-files', but fontifies
non-visited file names with the `ivy-virtual' face. With optional
argument NO-BUFFER, only list non-visited files."
Like `projectile-current-project-files', but skips any files
already being visited by a buffer."
(let ((root (projectile-project-root)))
(cl-loop
for name in (projectile-current-project-files)
for file = (expand-file-name name root)
if (not (get-file-buffer file))
collect (propertize name 'face 'ivy-virtual)
else
unless no-buffer
collect name)))
(defun counsel-projectile--find-file-action (file &optional other-window)
@ -94,6 +90,12 @@ argument NO-BUFFER, only list non-visited files."
`projectile-find-file-hook'."
(counsel-projectile--find-file-action file t))
(defun counsel-projectile--find-file-transformer (name)
"Transform non-visited file names with `ivy-virtual' face."
(if (not (get-file-buffer (expand-file-name name (projectile-project-root))))
(propertize name 'face 'ivy-virtual)
name))
;;;###autoload
(defun counsel-projectile-find-file (&optional arg)
"Jump to a project's file using completion.
@ -103,7 +105,7 @@ invalidates the cache first."
(interactive "P")
(projectile-maybe-invalidate-cache arg)
(ivy-read (projectile-prepend-project-name "Find file: ")
(counsel-projectile--file-list)
(projectile-current-project-files)
:matcher #'counsel--find-file-matcher
:require-match t
:keymap counsel-projectile-map
@ -115,6 +117,10 @@ invalidates the cache first."
'(("j" counsel-projectile--find-file-other-window-action
"other window")))
(ivy-set-display-transformer
'counsel-projectile-find-file
'counsel-projectile--find-file-transformer)
;;; counsel-projectile-find-dir
(defun counsel-projectile--dir-list ()
@ -370,15 +376,17 @@ invokes `projectile-commander' instead of
(counsel-projectile--buffer-list))
(mapc (lambda (file)
(add-text-properties 0 1 '(type file) file))
(counsel-projectile--file-list t))))
(counsel-projectile--unvisited-file-list))))
(defun counsel-projectile--transformer (str)
"Fontifies modified, file-visiting buffers.
Relies on `ivy-switch-buffer-transformer'."
(if (eq (get-text-property 0 'type str) 'buffer)
(ivy-switch-buffer-transformer str)
str))
(let ((type (get-text-property 0 'type str)))
(cond
((eq type 'buffer) (ivy-switch-buffer-transformer str))
((eq type 'file) (propertize str 'face 'ivy-virtual))
(t str))))
(defun counsel-projectile--matcher (regexp candidates)
"Return REGEXP-matching CANDIDATES.