aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBozhidar Batsov <bozhidar@toptal.com>2026-04-26 07:09:36 +0100
committerBozhidar Batsov <bozhidar@toptal.com>2026-04-26 07:09:36 +0100
commit183023602f1002f39d6e5c82ca44a317d6b517fe (patch)
tree0fc665f9a5cd458e78fe0c25f28f63614a2bb458
parent6bb29099114cf68077c4a15f1bde1b323cba5fe6 (diff)
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.
-rw-r--r--CHANGELOG.md1
-rw-r--r--projectile.el17
-rw-r--r--test/projectile-test.el4
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)