From 183023602f1002f39d6e5c82ca44a317d6b517fe Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sun, 26 Apr 2026 07:09:36 +0100 Subject: Use cons cells as projectile-project-root-cache keys Per-function entries are now keyed on (FUNC . DIR), and the overall failure marker on ('none . DIR), instead of formatted strings. Same equal semantics, no per-call format allocation, and lambda functions in projectile-project-root-functions hash by identity instead of by their printed repr. --- CHANGELOG.md | 1 + projectile.el | 17 +++++++---------- test/projectile-test.el | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b0cd1..2b64613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Changes +* `projectile-project-root-cache` now keys entries on cons cells (`(FUNC . DIR)` for per-function results, `('none . DIR)` for the overall failure marker) instead of formatted strings. This is internal state, but third-party code that reaches into the cache directly will need to update. * `projectile-parse-dirconfig-file' now returns a `projectile-dirconfig' struct (with `keep', `ignore', `ensure', and `prefixless-ignore' slots) instead of a positional 3-tuple. External callers should use the accessors (`projectile-dirconfig-keep' etc.) rather than `car'/`cadr'/`caddr'. * Soft-deprecate prefix-less ignore entries in `.projectile'. Lines without a `+'/`-'/`!' prefix are still treated as ignore patterns for backward compatibility, but a one-time warning is now shown for each project that uses them. Set `projectile-warn-on-prefixless-dirconfig-lines' to nil to silence. diff --git a/projectile.el b/projectile.el index cd3ea2a..bcd8302 100644 --- a/projectile.el +++ b/projectile.el @@ -1457,15 +1457,14 @@ If DIR is not supplied it's set to the current directory by default." (when (and (fboundp 'tramp-archive-file-name-p) (tramp-archive-file-name-p dir)) (setq dir (file-name-directory (tramp-archive-file-name-archive dir)))) - ;; the cached value will be 'none in the case of no project root (this is to - ;; ensure it is not reevaluated each time when not inside a project) so - ;; replace this 'none value with nil so a nil value is used instead + ;; The cached value is 'none when no project root was found (so we don't + ;; reevaluate every time when not inside a project); we map that back to + ;; nil for callers. Cache keys are conses: (FUNC . DIR) for per-function + ;; results, ('none . DIR) for the overall failure marker. (let ((result (or ;; if we've already failed to find a project dir for this ;; dir, and cached that failure, don't recompute - (let* ((cache-key (format "projectilerootless-%s" dir)) - (cache-value (gethash cache-key projectile-project-root-cache))) - cache-value) + (gethash (cons 'none dir) projectile-project-root-cache) ;; if the file isn't local, and we're not connected, don't try to ;; find a root now, but don't cache failure, as we might ;; re-connect. The `is-local' and `is-connected' variables are @@ -1482,7 +1481,7 @@ If DIR is not supplied it's set to the current directory by default." ;; through the project root functions until we find a project dir (seq-some (lambda (func) - (let* ((cache-key (format "%s-%s" 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 @@ -1493,9 +1492,7 @@ If DIR is not supplied it's set to the current directory by default." ;; if we get here, we have failed to find a root by all ;; conventional means, and we assume the failure isn't transient ;; / network related, so cache the failure - (let ((cache-key (format "projectilerootless-%s" dir))) - (puthash cache-key 'none projectile-project-root-cache) - 'none)))) + (puthash (cons 'none dir) 'none projectile-project-root-cache)))) (unless (eq result 'none) result)))) (defun projectile-ensure-project (dir) diff --git a/test/projectile-test.el b/test/projectile-test.el index b62224a..f099d5f 100644 --- a/test/projectile-test.el +++ b/test/projectile-test.el @@ -1236,7 +1236,7 @@ Just delegates OPERATION and ARGS for all operations except for`shell-command`'. ("projectA/src/") (let* ((projectile-project-root-functions '()) (dir "projectA/src") - (cache-key (format "projectilerootless-%s" dir)) + (cache-key (cons 'none dir)) (projectile-project-root-cache (make-hash-table :test 'equal))) (expect (gethash cache-key projectile-project-root-cache) :to-be nil) (expect (projectile-project-root dir) :to-be nil) @@ -1257,7 +1257,7 @@ Just delegates OPERATION and ARGS for all operations except for`shell-command`'. (lambda (&rest args) (eql 1 (length args))))) (let* ((projectile-project-root-functions '()) (dir "projectA/src") - (cache-key (format "projectilerootless-%s" dir)) + (cache-key (cons 'none dir)) (projectile-project-root-cache (make-hash-table :test 'equal))) (expect (gethash cache-key projectile-project-root-cache) :to-be nil) (expect (projectile-project-root dir) :to-be nil) -- cgit v1.0