aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-05 05:12:25 -0500
committerCraig Jennings <c@cjennings.net>2026-05-05 05:12:25 -0500
commit88b7fb502c0317ac29b3c7f3eb3f4c0617c87c02 (patch)
treef9cf670b800d2e303c26277e437d6f9cbfce0178
parent8bbb6d30529abd5350884e822b308da917966e73 (diff)
downloadorg-drill-88b7fb502c0317ac29b3c7f3eb3f4c0617c87c02.tar.gz
org-drill-88b7fb502c0317ac29b3c7f3eb3f4c0617c87c02.zip
fix: default DRILL_LEITNER_BOX to 0 in leitner-rebox
When the property is absent, org-entry-get returns nil and string-to-number errors with wrong-type-argument. Reachable when a user removes the property mid-session, or when a Leitner-tagged entry is rebox'd before its DRILL_LEITNER_BOX has been set. Wrapped the org-entry-get with (or ... "0"). Box 0 makes the rating semantics still sensible: a downgrade stays at 0, a promotion goes to 1.
-rw-r--r--org-drill.el7
-rw-r--r--tests/test-org-drill-leitner-rebox-missing-box.el43
2 files changed, 49 insertions, 1 deletions
diff --git a/org-drill.el b/org-drill.el
index f98bbfe..5037d73 100644
--- a/org-drill.el
+++ b/org-drill.el
@@ -3986,8 +3986,13 @@ shuffling is done in place."
(cond
((and (>= ch ?0) (<= ch ?5))
(let ((current-box
+ ;; Default to 0 if the property isn't set — `org-entry-get'
+ ;; returns nil and `string-to-number' would error on nil.
+ ;; Box 0 is sensible: a downgrade stays at 0, a promotion
+ ;; goes to 1.
(string-to-number
- (org-entry-get (point) "DRILL_LEITNER_BOX" nil))))
+ (or (org-entry-get (point) "DRILL_LEITNER_BOX" nil)
+ "0"))))
(cond
((or (= ch ?0))
(message "Refiled down to box: 1")
diff --git a/tests/test-org-drill-leitner-rebox-missing-box.el b/tests/test-org-drill-leitner-rebox-missing-box.el
new file mode 100644
index 0000000..68c49c9
--- /dev/null
+++ b/tests/test-org-drill-leitner-rebox-missing-box.el
@@ -0,0 +1,43 @@
+;;; test-org-drill-leitner-rebox-missing-box.el --- Regression for missing DRILL_LEITNER_BOX -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; `org-drill-leitner-rebox' read DRILL_LEITNER_BOX via `org-entry-get'
+;; and passed the result straight into `string-to-number'. When the
+;; property is absent, org-entry-get returns nil and string-to-number
+;; errors with "Wrong type argument: char-or-string-p, nil". Reachable
+;; if a user removes the property mid-session, or if a Leitner-tagged
+;; entry is rebox'd before its DRILL_LEITNER_BOX is set.
+;;
+;; Fix wraps the value with `(or ... "0")' so a missing property is
+;; treated as box 0 (which makes the rating semantics still sensible —
+;; a "downgrade by one" stays at 0, and a promotion goes to 1).
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org)
+(require 'org-drill)
+
+;;;; Regression
+
+(ert-deftest test-leitner-rebox-survives-missing-leitner-box-property ()
+ "On a leitner entry without DRILL_LEITNER_BOX, rebox should not crash."
+ (with-temp-buffer
+ (let ((org-startup-folded nil))
+ (insert "* Question :leitner:\nbody\n") ; no DRILL_LEITNER_BOX
+ (org-mode)
+ (goto-char (point-min))
+ (let ((session (org-drill-session)))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) "5"))
+ ((symbol-function 'sit-for) #'ignore))
+ ;; Should not error.
+ (org-drill-leitner-rebox session)
+ ;; After a quality-5 promotion from missing (treated as box 0):
+ ;; new box = 0 + 1 = 1. Either way, the property should now exist.
+ (should (org-entry-get (point) "DRILL_LEITNER_BOX")))))))
+
+(provide 'test-org-drill-leitner-rebox-missing-box)
+
+;;; test-org-drill-leitner-rebox-missing-box.el ends here