branch: elpa/projectile
commit 8f4a1f71cb9211229702d16e89c1c2e2cf31173f
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    Cache projectile-parse-dirconfig-file results per project
    
    The .projectile dirconfig file was being re-read and re-parsed 3-4
    times per indexing operation (by paths-to-ignore, patterns-to-ignore,
    paths-to-ensure, patterns-to-ensure, etc.).  This is wasteful,
    especially over TRAMP where each file read is a network round-trip.
    
    Cache the parsed result per project root, keyed by the file's
    modification time so edits to .projectile are picked up immediately.
    The cache is also cleared by projectile-invalidate-cache.
---
 projectile.el | 43 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 11 deletions(-)

diff --git a/projectile.el b/projectile.el
index d35df0094c..5323db0a0a 100644
--- a/projectile.el
+++ b/projectile.el
@@ -1119,6 +1119,7 @@ argument)."
     (remhash project-root projectile-project-type-cache)
     (remhash project-root projectile-projects-cache)
     (remhash project-root projectile-projects-cache-time)
+    (remhash project-root projectile--dirconfig-cache)
     ;; reset the project's cache file
     (when (projectile-persistent-cache-p)
       ;; TODO: Perhaps it's better to delete the cache file in such cases?
@@ -2127,18 +2128,13 @@ Unignored files/directories are not included."
   "Return the absolute path to the project's dirconfig file."
   (expand-file-name projectile-dirconfig-file (projectile-project-root)))
 
-(defun projectile-parse-dirconfig-file ()
-  "Parse project ignore file and return directories to ignore and keep.
+(defvar projectile--dirconfig-cache (make-hash-table :test 'equal)
+  "Cache for parsed dirconfig files, keyed by project root.
+Each value is a cons of (MTIME . PARSED-RESULT).")
 
-The return value will be a list of three elements, the car being
-the list of directories to keep, the cadr being the list of files
-or directories to ignore, and the caddr being the list of files
-or directories to ensure.
-
-Strings starting with + will be added to the list of directories
-to keep, and strings starting with - will be added to the list of
-directories to ignore.  For backward compatibility, without a
-prefix the string will be assumed to be an ignore string."
+(defun projectile--parse-dirconfig-file-uncached ()
+  "Parse the dirconfig file without caching.
+Returns a list of (KEEP IGNORE ENSURE) or nil if the file doesn't exist."
   (let (keep ignore ensure (dirconfig (projectile-dirconfig-file)))
     (when (projectile-file-exists-p dirconfig)
       (with-temp-buffer
@@ -2163,6 +2159,31 @@ prefix the string will be assumed to be an ignore 
string."
             (mapcar #'string-trim
                     (delete "" (reverse ensure)))))))
 
+(defun projectile-parse-dirconfig-file ()
+  "Parse project ignore file and return directories to ignore and keep.
+
+The return value will be a list of three elements, the car being
+the list of directories to keep, the cadr being the list of files
+or directories to ignore, and the caddr being the list of files
+or directories to ensure.
+
+Strings starting with + will be added to the list of directories
+to keep, and strings starting with - will be added to the list of
+directories to ignore.  For backward compatibility, without a
+prefix the string will be assumed to be an ignore string.
+
+Results are cached per project root and invalidated when the
+dirconfig file's modification time changes."
+  (let* ((dirconfig (projectile-dirconfig-file))
+         (project-root (projectile-project-root))
+         (cached (gethash project-root projectile--dirconfig-cache))
+         (mtime (file-attribute-modification-time (file-attributes 
dirconfig))))
+    (if (and cached (equal (car cached) mtime))
+        (cdr cached)
+      (let ((result (projectile--parse-dirconfig-file-uncached)))
+        (puthash project-root (cons mtime result) projectile--dirconfig-cache)
+        result))))
+
 (defun projectile-expand-root (name &optional dir)
   "Expand NAME to project root.
 When DIR is specified it uses DIR's project, otherwise it acts

Reply via email to