Fix: (--value-at) On cache miss, return nil when function does

Previously, on a cache miss, if FN returned nil, the sentinel would be
returned instead of nil.

Closes #78.  Thanks to Josh Moller-Mara (@mm--) for reporting.
This commit is contained in:
Adam Porter 2019-12-21 16:28:51 -06:00
parent b9dfd7d93b
commit 091a732a88
2 changed files with 44 additions and 32 deletions

View file

@ -447,17 +447,15 @@ Values compared with `equal'."
;; I'd like to use `-if-let*', but it doesn't leave non-nil variables ;; I'd like to use `-if-let*', but it doesn't leave non-nil variables
;; bound in the else clause, so destructured variables that are non-nil, ;; bound in the else clause, so destructured variables that are non-nil,
;; like found caches, are not available in the else clause. ;; like found caches, are not available in the else clause.
(if-let* ((buffer-cache (gethash (current-buffer) org-ql-node-value-cache)) (pcase (if-let* ((buffer-cache (gethash (current-buffer) org-ql-node-value-cache))
(modified-tick (car buffer-cache)) (modified-tick (car buffer-cache))
(position-cache (cdr buffer-cache)) (position-cache (cdr buffer-cache))
(buffer-unmodified-p (eq (buffer-modified-tick) modified-tick)) (buffer-unmodified-p (eq (buffer-modified-tick) modified-tick))
(value-cache (gethash position position-cache)) (value-cache (gethash position position-cache))
(cached-value (alist-get fn value-cache nil nil #'equal))) (cached-value (alist-get fn value-cache nil nil #'equal)))
;; Found in cache: return it. ;; Found in cache: return it.
(pcase cached-value cached-value
('org-ql-nil nil) ;; Not found in cache: call FN, cache and return its value.
(_ cached-value))
;; Not found in cache: get value and cache it.
(let ((new-value (or (funcall fn) 'org-ql-nil))) (let ((new-value (or (funcall fn) 'org-ql-nil)))
;; Check caches again, because it may have been set now, e.g. by ;; Check caches again, because it may have been set now, e.g. by
;; recursively going up an outline tree. ;; recursively going up an outline tree.
@ -477,7 +475,10 @@ Values compared with `equal'."
org-ql-node-value-cache)) org-ql-node-value-cache))
(map-put value-cache fn new-value) (map-put value-cache fn new-value)
(puthash position value-cache position-cache) (puthash position value-cache position-cache)
new-value))) new-value))
;; Return nil or the non-nil value.
('org-ql-nil nil)
(else else)))
(defun org-ql--add-markers (element) (defun org-ql--add-markers (element)
"Return ELEMENT with Org marker text properties added. "Return ELEMENT with Org marker text properties added.

View file

@ -143,6 +143,7 @@ RESULTS should be a list of strings as returned by
sum 1))))) sum 1)))))
(describe "Caching" (describe "Caching"
(it "Clears value cache after buffer changes" (it "Clears value cache after buffer changes"
;; See <https://github.com/alphapapa/org-ql/issues/59>. ;; See <https://github.com/alphapapa/org-ql/issues/59>.
(with-temp-buffer (with-temp-buffer
@ -159,7 +160,17 @@ RESULTS should be a list of strings as returned by
(goto-char (point-min)) (goto-char (point-min))
(org-ql--value-at (point-min) #'point) (org-ql--value-at (point-min) #'point)
(expect (org-ql--value-at (point-min) #'org-get-heading) (expect (org-ql--value-at (point-min) #'org-get-heading)
:to-equal "Heading 2")))) :to-equal "Heading 2")))
(it "Returns nil when cache misses and function returns nil"
;; See <https://github.com/alphapapa/org-ql/pull/78>.
(with-temp-buffer
(org-mode)
(insert "* Heading 1")
;; FIXME: `--value-at' does not actually move point, so we do it here.
(goto-char (point-min))
(expect (org-ql--value-at (point-min) #'org-get-local-tags)
:to-be nil))))
(describe "Query functions/macros" (describe "Query functions/macros"