aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-05 05:14:09 -0500
committerCraig Jennings <c@cjennings.net>2026-05-05 05:14:09 -0500
commitaa3d724c6bdd3a16391ad95040184c118386d329 (patch)
treeaaa706e89f60eb3dbbe1eb524dcb7921556cdf48
parent88b7fb502c0317ac29b3c7f3eb3f4c0617c87c02 (diff)
downloadorg-drill-aa3d724c6bdd3a16391ad95040184c118386d329.tar.gz
org-drill-aa3d724c6bdd3a16391ad95040184c118386d329.zip
fix: hide-drawers ignores drawers with no :END:
drawer-end was captured as (save-excursion (re-search-forward ':END:' end t) (point)) which always returns a number — (point) is always defined. The subsequent (when drawer-end ...) guard was dead, so a malformed drawer (typo in :END:, mid-edit truncation) ended up with a junk overlay covering whatever range point happened to land in. Captured the search result itself and gate on it. Malformed drawers are now skipped silently; well-formed drawers still get their normal overlay.
-rw-r--r--org-drill.el13
-rw-r--r--tests/test-org-drill-hide-drawers-missing-end.el66
2 files changed, 76 insertions, 3 deletions
diff --git a/org-drill.el b/org-drill.el
index 5037d73..97ba891 100644
--- a/org-drill.el
+++ b/org-drill.el
@@ -1941,15 +1941,22 @@ visual overlay, or with the string TEXT if it is supplied."
(defun org-drill-hide-drawers ()
"Hide all drawers in the current entry, including PROPERTIES drawer.
-This is more reliable than `org-cycle-hide-drawers' for drill display."
+This is more reliable than `org-cycle-hide-drawers' for drill display.
+
+Drawer-end was previously captured as the value of `(point)' after
+the search — always a number, so the subsequent `(when drawer-end)'
+guard was dead. Capture the search result itself and gate on that,
+otherwise a malformed drawer (no `:END:') ends up with a junk
+overlay covering whatever range point happened to be at."
(save-excursion
(org-back-to-heading t)
(let ((end (save-excursion (org-end-of-subtree t t))))
(while (re-search-forward org-drawer-regexp end t)
(let* ((drawer-start (match-beginning 0))
(drawer-end (save-excursion
- (re-search-forward "^[ \t]*:END:[ \t]*$" end t)
- (point))))
+ (when (re-search-forward
+ "^[ \t]*:END:[ \t]*$" end t)
+ (point)))))
(when drawer-end
(org-drill-hide-region drawer-start drawer-end)))))))
diff --git a/tests/test-org-drill-hide-drawers-missing-end.el b/tests/test-org-drill-hide-drawers-missing-end.el
new file mode 100644
index 0000000..fff1610
--- /dev/null
+++ b/tests/test-org-drill-hide-drawers-missing-end.el
@@ -0,0 +1,66 @@
+;;; test-org-drill-hide-drawers-missing-end.el --- Regression for missing :END: -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; `org-drill-hide-drawers' captured drawer-end as
+;; (save-excursion (re-search-forward ":END:" end t) (point))
+;; which always returns a number — `(point)' is always defined. The
+;; subsequent `(when drawer-end ...)' guard was dead code, so a
+;; malformed drawer without `:END:' would either no-op silently or
+;; create a zero/negative-width overlay (depending on point state).
+;;
+;; The user-visible failure mode: a drawer with a typo or
+;; mid-edit truncation gets the body kept visible during the drill
+;; (which is intended), but the function does junk overlay work to
+;; get there. Captured the search result and gate on that.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org)
+(require 'org-drill)
+
+;;;; Regression
+
+(ert-deftest test-hide-drawers-malformed-drawer-without-end-creates-no-overlay ()
+ "A drawer that opens but never closes should not produce a hidden-text
+overlay (and should not error)."
+ (with-temp-buffer
+ (let ((org-startup-folded nil))
+ ;; Note: no :END:. Just :NOTANEND: as a near-miss.
+ (insert "* Question :drill:\n:PROPERTIES:\n:ID: x\n:NOTANEND:\nbody\n")
+ (org-mode)
+ (goto-char (point-min))
+ (org-drill-hide-drawers)
+ ;; Count overlays — there should be none from the malformed drawer.
+ (let ((n 0))
+ (dolist (ovl (overlays-in (point-min) (point-max)))
+ (when (eql 'org-drill-hidden-text-overlay (overlay-get ovl 'category))
+ (cl-incf n)))
+ ;; Either no overlays at all (drawer skipped entirely), or only
+ ;; well-formed drawers were hidden. No malformed-drawer overlay.
+ (should (= 0 n))))))
+
+(ert-deftest test-hide-drawers-malformed-followed-by-wellformed-still-hides-good ()
+ "Even after encountering a malformed drawer, well-formed drawers still hide."
+ (with-temp-buffer
+ (let ((org-startup-folded nil))
+ (insert "* Question :drill:
+:LOGBOOK:
+note one
+:END:
+body line
+")
+ (org-mode)
+ (goto-char (point-min))
+ (org-drill-hide-drawers)
+ ;; The LOGBOOK drawer is well-formed → 1 overlay expected.
+ (let ((n 0))
+ (dolist (ovl (overlays-in (point-min) (point-max)))
+ (when (eql 'org-drill-hidden-text-overlay (overlay-get ovl 'category))
+ (cl-incf n)))
+ (should (>= n 1))))))
+
+(provide 'test-org-drill-hide-drawers-missing-end)
+
+;;; test-org-drill-hide-drawers-missing-end.el ends here