aboutsummaryrefslogtreecommitdiff
path: root/projectile.el
diff options
context:
space:
mode:
authorBozhidar Batsov <bozhidar@batsov.dev>2026-02-28 08:42:17 +0200
committerBozhidar Batsov <bozhidar@batsov.dev>2026-02-28 08:54:10 +0200
commit8f4a1f71cb9211229702d16e89c1c2e2cf31173f (patch)
tree70f05b5c5093a018b40522c88b35fb1a0a255dcc /projectile.el
parentb5936d67c70f5d18094d31eb488304a9a0be8d16 (diff)
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.
Diffstat (limited to 'projectile.el')
-rw-r--r--projectile.el43
1 files changed, 32 insertions, 11 deletions
diff --git a/projectile.el b/projectile.el
index d35df00..5323db0 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