From 77eb4f046cc90d535ba30ec8a408f52cb85da63f Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 5 May 2026 03:43:25 -0500 Subject: fix: include child subtree in entry-empty-p search bound (upstream #13) kqr (2019-07-22) reported that drill entries whose answer lives inside a child sub-heading were silently skipped. Their example: a question in the heading text and the answer under `** The Answer`. The function returned t (empty) for such entries, so they never got presented during drill sessions. The cause is `(outline-next-heading)` in `org-drill-entry-empty-p`. That primitive lands on the first heading at any level, including children. So the search range was metadata-end up to the child's heading line, which excluded the child's body. Bodies that lived in child sub-headings never got searched. I switched the bound to `(org-end-of-subtree t t)`, which covers the whole subtree of the current heading and degrades gracefully at the last heading in the buffer. The reporter suggested `outline-forward-same-level`, but that primitive errors at the last sibling, which would be its own regression. `org-end-of-subtree` is the canonical Emacs idiom for this kind of bound and handles end-of-buffer correctly. I added `tests/test-org-drill-entry-empty-p.el` with 6 ERT tests across Normal, Boundary (kqr's exact fixture), and edge categories. The two regression tests fail at HEAD before the fix and pass after. One semantic note worth flagging: any subtree content now counts as non-empty, including bare child headings with no body of their own. The bug report is silent on that case and I expect it to be rare in practice. If anyone reports the new behavior as a regression, the fix would be to filter heading lines out of the graphical-character search. --- org-drill.el | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'org-drill.el') diff --git a/org-drill.el b/org-drill.el index 07de25d..c3a73eb 100644 --- a/org-drill.el +++ b/org-drill.el @@ -2157,11 +2157,17 @@ Note: does not actually alter the item." ;; This version is about 5x faster than the old version, above. (defun org-drill-entry-empty-p () - "Return non-nil if the current entry is empty." + "Return non-nil if the current entry is empty. + +The search bound covers the whole subtree, so an entry whose answer +lives inside a child sub-heading is correctly reported as non-empty +\(upstream issue #13). `org-end-of-subtree' is used in place of +`outline-next-heading' because the latter lands on the first child +heading, which truncates the search range before the child's body." (save-excursion (org-back-to-heading t) (let ((lim (save-excursion - (outline-next-heading) (point)))) + (org-end-of-subtree t t) (point)))) (if (fboundp 'org-end-of-meta-data-and-drawers) (org-end-of-meta-data-and-drawers) ; function removed Feb 2015 (org-end-of-meta-data t)) -- cgit v1.2.3