aboutsummaryrefslogtreecommitdiff
path: root/projectile.el
diff options
context:
space:
mode:
authorBozhidar Batsov <bozhidar@toptal.com>2026-04-26 07:14:41 +0100
committerBozhidar Batsov <bozhidar@toptal.com>2026-04-26 07:14:41 +0100
commit69adda8f55aaeedcee77948318c3e389665019db (patch)
tree90b33a5edeea12c5ca76f381e40234a2293493d8 /projectile.el
parent67981d6d93b957edf3359d94f6216837427834bc (diff)
Memoize per-function nil results in the root cache (#1836)
Previously a root function returning nil stored nil in the cache, which is indistinguishable from a missing entry, so on the next call the function was re-run. When a project's root is found by the third function in projectile-project-root-functions, the first two re-walked the tree on every call - and projectile-project-root is hot in mode-line / company / spaceline updates. Store the symbol 'none for unsuccessful entries and treat it as a cache hit on lookup. The overall-failure marker already used 'none in a separate slot, so this just extends the same convention to the per-function entries.
Diffstat (limited to 'projectile.el')
-rw-r--r--projectile.el15
1 files changed, 10 insertions, 5 deletions
diff --git a/projectile.el b/projectile.el
index 0530925..d88819f 100644
--- a/projectile.el
+++ b/projectile.el
@@ -1482,17 +1482,22 @@ If DIR is not supplied it's set to the current directory by default."
;; `projectile-root-local' reads a buffer-local variable rather
;; than inspecting DIR, so its result must not be cached - two
;; buffers in the same directory can legitimately disagree.
+ ;; For other functions, both successes and per-function failures
+ ;; (stored as the 'none sentinel) are memoized, so functions
+ ;; earlier in the list that returned nil aren't re-walked on
+ ;; every call.
(seq-some
(lambda (func)
(if (eq func 'projectile-root-local)
(funcall func dir)
(let* ((cache-key (cons func dir))
(cache-value (gethash cache-key projectile-project-root-cache)))
- (if (and cache-value (file-exists-p cache-value))
- cache-value
- (let ((value (funcall func (file-truename dir))))
- (puthash cache-key value projectile-project-root-cache)
- value)))))
+ (cond
+ ((eq cache-value 'none) nil)
+ ((and cache-value (file-exists-p cache-value)) cache-value)
+ (t (let ((value (funcall func (file-truename dir))))
+ (puthash cache-key (or value 'none) projectile-project-root-cache)
+ value))))))
projectile-project-root-functions)
;; if we get here, we have failed to find a root by all
;; conventional means, and we assume the failure isn't transient