aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* refactor: add org-drill-card-state struct + ADRCraig Jennings2026-05-271-0/+22
| | | | | | First step of #147. I added a cl-defstruct bundling the recall fields the schedulers and the item-data round-trip shuttle around (last-interval, repetitions, ease, failures, meanq, total-repeats), with an ADR comment recording why: the same fields were passed as positional lists in three different orderings, and three call sites re-ordered between them by hand. The struct is inert here. The item-data round-trip and the schedulers adopt it in the following commits.
* refactor: sharpen opaque local variable names across org-drill.elCraig Jennings2026-05-271-39/+39
| | | | | | I renamed several cryptic locals to say what they hold: idx to index-var in the pop-random gensym, val to raw-value in the five DRILL_* property getters, q to quality in hypothetical-next-review-dates, m to marker in free-markers, the a/b sort and filter lambdas in order-overdue-entries to entry/other, and dat to card-def in the empty-card-type check. These are pure renames with no behavior change. Byte-compile and the full unit suite stay green.
* refactor: name the progress-meter chars in org-drill-progress-messageCraig Jennings2026-05-271-4/+4
| | | | sym1/sym2 gave no hint of their role. current-meter-char toggles per meter wrap and alternate-meter-char is its inverse, which is what the two make-string calls actually consume.
* ci: bump checkout to v5 and force node24 for setup-easkCraig Jennings2026-05-271-6/+13
| | | | actions/checkout@v4 and emacs-eask/setup-eask@master both run on the node20 runtime GitHub force-migrates to node24 on 2026-06-02. checkout@v5 ships the node24 runtime, so I bumped it across all three jobs. setup-eask has no node24 release yet, so I set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 at the workflow level to run it under node24 now, ahead of the forced switch rather than after.
* fix: guard sm2 cl-assert error tests behind Emacs-30 skipCraig Jennings2026-05-271-6/+23
| | | | | | The three error-case tests in test-org-drill-determine-next-interval-sm2.el asserted their cl-assert preconditions with a bare should-error :type 'cl-assertion-failed. On Emacs 29, ERT installs a signal-hook that keeps should-error from matching the condition, so the tests failed the 29.4 job outright. Under undercover's edebug instrumentation the same assert dropped into a blocking debugger in batch mode, which hung the coverage job until the 6-hour timeout on every push. The sm5 and simple8 files already wrap their equivalent error tests in a skip-unless guard for this quirk. I copied that helper into sm2 so all three scheduler files handle Emacs 29 the same way.
* chore: gitignore the inbox dir and editor backup filesCraig Jennings2026-05-271-0/+2
| | | | inbox/ holds local Claude handoff files and *~ are Emacs backups; neither belongs in the repo.
* feat: undo last rating, customizable keys, and configurable text limitCraig Jennings2026-05-274-49/+323
| | | | | | | | | | | | | | A batch of self-contained user-facing improvements, squashed from the feat/org-drill-solo-features branch. I added an undo for the last rating (issue #2 follow-up). The rating prompt now takes an undo key (org-drill--undo-key, default u): it restores the previous card's scheduling snapshot, drops the recorded quality, and re-queues that card, then returns to the current prompt. Each rating snapshots the scheduling properties and SCHEDULED line onto a per-session stack capped at org-drill-undo-limit (default 3). org-drill-reschedule loops on the rating read so undo doesn't rate the current card. I made the five session-control keys (quit, edit, help, skip, tags) defcustoms so they can be rebound from customize-group (issue #35), keeping their defaults. The 0-5 rating keys stay as-is, since they're tied to the quality scale rather than being variables. I lifted the hardcoded 100-line entry-text limit in org-drill-get-entry-text into the org-drill-entry-text-max-lines defcustom, defaulting to 100. I also deleted a commented-out old org-entry-empty-p that the real definition had already replaced. Existing tests stay green and each change added its own, including snapshot/restore and prompt-loop tests for undo.
* refactor: dedupe presenters, group defcustoms, and fill in docstringsCraig Jennings2026-05-265-232/+428
| | | | | | | | | | | | | | A cleanup pass over org-drill internals, squashed from the refactor/wave3-cleanup branch. No behavior change. Each step kept the existing tests green and added its own. I shared two duplicated helpers across the language card getters: org-drill--read-property-string and org-drill--face-from-alist. I factored the cloze body-scan out of the two multicloze presenters into org-drill--cloze-body-bounds, org-drill--count-cloze-matches, and org-drill--hide-cloze-by-index, so each presenter just picks which indices to hide. I pulled the presenter resolution and the four-way result classification out of org-drill-entry-f into org-drill--resolve-presenter and org-drill--classify-presentation-result, untangling the pivot of every drill iteration. I split the 37 defcustoms (and the three cloze faces) into four customize sub-groups (display, algorithm, session, leech) so customize-group org-drill is navigable. There's no leitner group because the Leitner settings are defvars. I documented the 22 defuns that had no docstring, rewrote the corrupted org-drill-presentation-prompt-in-mini-buffer docstring, and switched eleven docstrings to the imperative "Return" (issue #2).
* fix: scope cloze fontification to drill buffers via org-drill-modeCraig Jennings2026-05-264-30/+230
| | | | | | | | org-drill-add-cloze-fontification ran on org-font-lock-set-keywords-hook, which fires in every org buffer, and pushed the cloze rule into org's global org-font-lock-extra-keywords. The cloze regexp is built from the [ and ] delimiters, so an org priority cookie like [#A] matched the cloze pattern and got fontified as a cloze in every org buffer, colliding with org's headline fontification and stripping the heading's org-level-N face. I replaced the global install with org-drill-mode, a buffer-local minor mode that adds the cloze keywords only to its own buffer via font-lock-add-keywords. org-drill-auto-enable-mode (default on) turns the mode on from org-mode-hook in buffers that hold drill cards, so existing drill files keep their cloze highlighting while plain org buffers stay clean. Highlighting still respects org-drill-use-visible-cloze-face-p. The cloze regexp itself is unchanged, so the single-line cloze constraint from #38 is preserved.
* chore: coverage, autoload fix, and internal cleanup for org-drillCraig Jennings2026-05-2611-41/+345
| | | | | | | | | | | | | | | | A batch of test-coverage and hardening work, squashed from the test-work branch. Tests: deduplicated a colliding leitner-capture test name so make test-name loads again. Added SM2 assert-failure cases, the six basic multicloze variant delegations, the three English-side spanish-verb branches, and org-drill-current-scope branch coverage. Fix: the entry-point commands (org-drill itself, cram-tree, tree, directory, resume, relearn-item, strip-all-data, merge-buffers) carried no autoload cookies, so M-x failed from a fresh install until something pulled the file in. They're autoloaded now. Perf: org-drill-shuffle was quadratic because it indexed a list with elt on every swap. It runs a linear Fisher-Yates pass over a vector now, and it checks its argument is a list. Feat: added org-drill-version, a constant plus an interactive command, so a bug reporter doesn't have to open the file header. Refactor: extracted org-drill--format-tense-mood, shared by the two verb-conjugation presenters that each carried a copy. Docs: explained the SM8 magic numbers in the simple8 helpers as empirical fits rather than tunable knobs.
* test: fix date-sensitive days-since-creation assertionCraig Jennings2026-05-111-7/+13
| | | | | | test-org-drill-entry-days-since-creation-with-date-added pinned DATE_ADDED to a hardcoded 2026-04-01 and asserted 33-35 days, which only holds when the wall clock is near 2026-05-05. The with-fixed-now mock that was supposed to make it deterministic rebinds `current-time`. But the DATE_ADDED branch of org-drill-entry-days-since-creation goes through `org-time-stamp-to-now`, which reads the real clock. The rebind never reaches it. CI went red once the calendar moved past the +35-day window. I rewrote the test to derive DATE_ADDED 34 calendar days before today (via decode-time / encode-time, so month rollover and DST are handled) and assert the function returns exactly 34. No mock needed. Both sides read the same real clock.
* ci: install Eask instead of Cask in workflow setupCraig Jennings2026-05-101-6/+6
| | | | | | The Cask→Eask migration in d012894 switched the Makefile to drive Eask, but ci.yml still installed Cask via cask/setup-cask. All three jobs (test, lint, coverage) failed at `make setup` with "Eask not found". I swapped each job to use emacs-eask/setup-eask. No other CI logic changed.
* build: migrate from Cask to EaskCraig Jennings2026-05-106-37/+56
| | | | | | | | | | Cask's upstream has slowed. Eask is the actively maintained successor. Eask's `package-file` directive doesn't auto-install the deps from the .el header's Package-Requires, so the Eask file mirrors emacs/seq/org/persist explicitly. `eask install-deps` also doesn't pull transitive deps, so dash, m-buffer, and shut-up needed their own `depends-on` lines for undercover and elisp-lint to activate. The Makefile swaps are mechanical: $(CASK) → $(EASK), `cask install` → `eask install-deps --dev`, `cask build` → `eask compile`. The URL in org-drill.el's header pointed at the abandoned upstream's GitLab issues page. Eask cross-validates that against website-url, so I updated it to the GitHub mirror — where users file issues now.
* test: cover the active-input-method branch of response-get-buffer-createCraig Jennings2026-05-051-0/+12
| | | | | | | | I added a small case for `org-drill-response-get-buffer-create' that sets an input method then asserts the helper propagates it into the new response buffer. Coverage stayed near 94.7% (instrumentation noise can move it ±0.2%).
* test: cover present-simple-card-with-typed-answerCraig Jennings2026-05-051-0/+18
| | | | | | | | | I added a test for `org-drill-present-simple-card-with-typed-answer' that mocks `prompt-for-string' and the surrounding hide-* helpers, then asserts the presenter forwards the session and returns the prompt's result. Coverage moved from 94.4% to 94.8%.
* test: cover org-drill-presentation-prompt-in-bufferCraig Jennings2026-05-051-0/+61
| | | | | | | | | | I extended the presentation-prompt tests with cases for the in-buffer variant: default prompt assembled when none is supplied, explicit prompt fed straight through, drill-answer cleared on entry, and the session's exit-kind flowing back to the caller after recursive-edit returns. Recursive-edit, display-buffer, and the timer are all mocked. Coverage moved from 93.2% to 94.4%.
* test: cover org-drill-test-display dev helperCraig Jennings2026-05-051-0/+30
| | | | | | | | | | I added a test for the developer helper `org-drill-test-display' that mocks `org-drill-entry-f' to confirm the dispatcher fires and that the zysygy tag is toggled off again on exit. I also added a small case for `org-drill-test-display-rescheduler' that verifies it runs `org-drill-display-answer-hook' and waits on read-key-sequence. Coverage moved from 92.7% to 93.2%.
* test: cover all navigation arrow keys in --read-rating-keyCraig Jennings2026-05-051-0/+9
| | | | | | | | | I extended the existing rating-key test with a dolist that fires every remaining navigation key (up, left, right, prior, next) — previously only `down' was covered. Each key advances the loop without ending it, then a numeric input terminates. Coverage moved from 92.3% to 92.7%.
* test: cover org-drill-add-cloze-fontification flag branchCraig Jennings2026-05-051-0/+19
| | | | | | | | | I added a couple of cases for `org-drill-add-cloze-fontification': when `org-drill-use-visible-cloze-face-p' is t, a fontification spec gets pushed onto `org-font-lock-extra-keywords'; with the flag nil, the list is left alone. Coverage moved from 92.1% to 92.3%.
* test: cover SM5 random-noise, --read-key-sequence input-method dance, ↵Craig Jennings2026-05-051-0/+79
| | | | | | | | | | | | | goto-heading error I added small-branch tests for the SM5 dispersal-factor multiplier when `org-drill-add-random-noise-to-intervals-p' is t, `org-drill--read-key-sequence' deactivating/reactivating an active input method (and skipping the dance when none is active), and the error branch in `org-drill-goto-drill-entry-heading' when no parent heading carries the drill tag. Coverage moved from 92.1% to 92.1%.
* test: cover --pick-next-marker and resume happy-pathsCraig Jennings2026-05-052-0/+96
| | | | | | | | | | | | | I extended `tests/test-org-drill-route-rating-result.el' with four `org-drill--pick-next-marker' cases (no resume → pop, resume with live drill marker → keep current-item and clear resume-p, resume with nil or non-drill current-item → fall through to fresh pop). I also extended the resume regression file with the three happy-path branches of `org-drill-resume': pending entries → resume, finished with pending count → y-or-n-p offers a new session, finished with nothing → print 'finished'. Coverage moved from 91.7% to 92.1%.
* test: cover --route-rating-result quit/edit/skip/next branchesCraig Jennings2026-05-051-0/+71
| | | | | | | | | | | I added tests for `org-drill--route-rating-result' covering all four return values: nil → 'quit (end-pos becomes :quit), 'edit → 'edit (end-pos becomes a marker), 'skip → 'skip (current-item cleared), quality 0 → 'next (pushes to again-entries) and quality 5 → 'next (pushes to done-entries). Also a case where again-entries is non-empty so the shuffle branch runs. Coverage moved from 91.6% to 91.7%.
* test: cover the leitner main entry orchestratorCraig Jennings2026-05-051-0/+106
| | | | | | | | | | I added tests for `org-drill-leitner' with mocked `leitner-entry' returning t (full loop completes, summary printed), 'quit (pcase quit branch returns t), 'edit (pcase edit branch jumps to marker), and a case where the boxed queue is short of `org-drill-maximum-items-per-session' so `leitner-start-box' runs to top it up. Coverage moved from 89.8% to 91.6% — the suite is now over 90%.
* test: cover the org-drill main entry and hypothetical-next-review-date dispatchCraig Jennings2026-05-052-0/+129
| | | | | | | | | | | I added tests for the public `org-drill' command that mock `org-drill-entries' so the orchestrator runs in batch: empty buffer → 'no pending' message, populated buffer → entry loop runs, cram=t flag flips the session's cram-mode slot, resume-p skips entry collection. I also extended the cloze + scheduling helpers file with sm2 and simple8 coverage for `org-drill-hypothetical-next-review-date'. Coverage moved from 89.0% to 89.8%.
* test: cover replace-entry-text multi mode, multicloze weight errors, ↵Craig Jennings2026-05-051-0/+93
| | | | | | | | | | | | | copy-to-buffer fallback I added tests for `org-drill-replace-entry-text' with the multi-p flag (list of replacements creates multiple overlays) and the simple single-overlay case, the multicloze weight-validation error branches in `-firstmore' and `-firstless', and the `org-drill-copy-entry-to-other-buffer' recovery path that appends to the end of DEST when the source's outline path doesn't exist there. Coverage moved from 88.6% to 89.0%.
* test: cover leech-warning, minibuffer timer, cloze length flag, simple8 noiseCraig Jennings2026-05-051-0/+115
| | | | | | | | | | | I added tests for `org-drill--maybe-prepend-leech-warning' (three branches: not a leech, method not warn, leech with warn), the presentation minibuffer-timer function (emits MM:SS prompt, cancels after 10 calls, switches to '++:++' after an hour), the `org-drill-cloze-length-matches-hidden-text-p' branch (display becomes a dotted string), and the simple8 random-noise dispersal-factor branch. Coverage moved from 87.8% to 88.6%.
* test: cover marker end-pos, variable-pitch restore, young/overdue queue branchesCraig Jennings2026-05-053-0/+104
| | | | | | | | | | | I extended three existing test files with cases that hit branches the suite was missing: `org-drill--show-end-message' with a live-marker end-pos (jumps to the marker), `org-drill--restore-display' for the variable-pitch-on / variable-pitch-off / text-scale paths, and `org-drill-pop-next-pending-entry' for the young-mature and overdue branches in the queue priority cond. Coverage moved from 87.0% to 87.8%.
* test: cover leitner-start-box and --read-rating-key input shapesCraig Jennings2026-05-052-0/+198
| | | | | | | | | | I added tests for `org-drill-leitner-start-box' (move N entries from unboxed into box 1, respect the count arg, zero is a noop) and for `org-drill--read-rating-key' (string input, arrow vector, wheel-event vector, help key showing help block, tags key triggering set-tags, typed-answer rendering in the prompt). Coverage moved from 85.7% to 87.0%.
* test: cover merge-buffers, all-leitner-capture, leitner-vs-drill summaryCraig Jennings2026-05-051-0/+142
| | | | | | | | | I added tests for `org-drill-merge-buffers' (yes/no confirmation, defaulting dest to current buffer, full migrate pipeline), `org-drill-all-leitner-capture' (populate and reverse boxed/unboxed queues), and the `org-drill-leitner-vs-drill-entries' summary message. Coverage moved from 82.4% to 85.7%.
* test: cover --setup-display, --restore-display, --migrate-from-source, ↵Craig Jennings2026-05-052-0/+214
| | | | | | | | | | | scope=directory I added unit tests for the display-state helpers (capture text scale, variable-pitch, modeline; restore them on session exit), the directory branch of `org-drill-current-scope', and `--migrate-from-source''s three-branch cond (matching ID, no ID, ignore-new-items). Coverage moved from 81.8% to 82.4%.
* test: cover map-leitner-capture, response-mode, timer, sm2/simple8 dispatchCraig Jennings2026-05-053-0/+210
| | | | | | | | | I added direct tests for `org-drill-map-leitner-capture' (route by DRILL_LEITNER_BOX), the response-mode exit-kind handlers (quit/edit/skip/ tags/rtn), `org-drill-presentation-timer-cancel', and the sm2/simple8 branches of `org-drill-smart-reschedule''s algorithm dispatcher. Coverage moved from 80.6% to 81.8%.
* test: cover orchestration helpers extracted during refactorCraig Jennings2026-05-051-0/+245
| | | | | | | | | | | | I added 16 ERT tests for the helpers carved out of org-drill and org-drill-merge-buffers in the recent refactor pass: prepare-fresh-session, queues-empty-p, collect-entries, show-resume-hint, show-end-message, build-dest-id-table, copy-scheduling-to-marker, and strip-unmatched-dest-entries. The clean-completion test for show-end-message binds org-drill-save-buffers-after-drill-sessions-p to nil so the dispatcher doesn't trip save-some-buffers' interactive prompt under batch ERT.
* chore: replace apple.jpg with public-domain USDA illustration (upstream #34)Craig Jennings2026-05-052-0/+19
| | | | | | | | | | | | | | | | | | | The previously-bundled apple.jpg had unclear redistribution rights — flagged in upstream issue #34 as a blocker for clean MELPA hosting and downstream packaging. Replaced with USDA 2026 Apple from the U.S. Department of Health and Human Services' Dietary Guidelines for Americans 2025-2030, sourced from Wikimedia Commons. Public domain under 17 USC §105 (work of the U.S. federal government), no attribution required. Source PNG resized to 298x348 (matching the original's footprint) and flattened to a white-background JPEG so the existing spanish.org and robot/spanish-robot.org file: links keep working without modification. Documented the bundled-content provenance in assets/README.md alongside the made-for-emacs-badge.svg.
* ci: drop Emacs 28.2 from matrix; skip cl-assert tests on Emacs 29Craig Jennings2026-05-053-33/+32
| | | | | | | | | | | | | | | | | | | | | | | Two pragmatic changes after watching CI fail repeatedly: CI matrix: dropped 28.2. Emacs 28 ships Org 9.5; Cask is supposed to pull our declared org>=9.6 over the built-in but doesn't reliably in this CI setup, and several test categories use APIs/behaviors (cl-letf on signal-hook-function, eieio idioms, modern org-fold-*) that don't quite work on 28. Practical floor is now Emacs 29 (ships Org 9.6 built-in). Matrix is 29.4 + snapshot. Scheduler error tests: added skip-unless (>= emacs-major-version 30) to the test-scheduler--should-cl-assert helper in both simple8 and sm5 test files. ERT 29 installs an aggressive signal-hook-function around the entire ert-deftest body that intercepts every signal before any inner condition-case runs; shadowing the hook locally doesn't help (verified across four attempts). The eight cl-assert-precondition tests now run on Emacs 30+ where ERT's hook leaves inner condition-case alone, and skip on 29.x. All other tests still run on 29.4. Locally green. Pushing to verify CI.
* test: shadow signal-hook-function so cl-assertion-failed can be caughtCraig Jennings2026-05-052-17/+28
| | | | | | | | | | | | | | | | | | | | Attempt 4 at making the eight scheduler error tests pass on Emacs 29.4 in CI. The earlier approaches kept failing because ERT 29.4 installs ert--should-signal-hook as signal-hook-function around the entire ert-deftest body — not just inside should forms. That hook fires on every signal before any inner condition-case can catch it, which is why even a bare (condition-case ... (cl-assertion-failed nil)) at the top of the test body didn't work. The new helper rebinds signal-hook-function to nil inside its own let-scope, so condition-case catches the cl-assertion-failed signal normally. The ert-fail on the no-error path runs outside that shadowing scope, so it still routes through ERT's failure handling. Locally green; pushing to test 29.4 in CI.
* test: catch cl-assertion-failed by name without ERT should-wrappingCraig Jennings2026-05-052-20/+24
| | | | | | | | | | | | | | | | The previous fix wrapped condition-case in (should (eq 'caught ...)), but ERT in Emacs 29.4 installs signal-hook-function around should forms — that hook fires on every signal, intercepting them before the inner condition-case can catch. CI on 29.4 still failed. This iteration drops should entirely. Each test body becomes a plain condition-case at the top level: run the form, and if it returns normally, ert-fail. Catch cl-assertion-failed by name rather than via the error parent — its parent-class registration is inconsistent across Emacs versions, but the symbol-name match through condition-case always works. Locally green; let's see what 29.4 does with it.
* docs: add LICENSE, CONTRIBUTING, CHANGELOG, and NEWS filesCraig Jennings2026-05-054-0/+880
| | | | | | | | | | | | | | | | | | | | | Four new top-level docs to round out the project's public-facing materials: - LICENSE: full GPL-3 text, mirroring the header notice in org-drill.el (copied from emacs-wttrin's bundled license). - CONTRIBUTING.md: bug-reporting guide, patch flow (fork → branch → tests → PR), development setup instructions wrapping the existing make targets, and testing/style notes pointing at the TDD discipline expected. - CHANGELOG.md: keepachangelog format with an Unreleased section documenting every change since the fork — bug fixes by issue number, new infrastructure (CI, coverage, lint), structural refactors, and removals. - NEWS: shorter user-facing summary highlighting the bugs most likely to have bitten existing users. Closes the [#A] TODO 'Add CONTRIBUTING, CHANGELOG, NEWS, and a standalone LICENSE file'.
* test: replace should-error with manual condition-case in scheduler error testsCraig Jennings2026-05-052-24/+40
| | | | | | | | | | | | | | | | | | | The :type 'cl-assertion-failed' fix didn't help — Emacs 29.4 in CI still marks the eight scheduler error tests as failures even though the cl-assertion-failed signal clearly fires (visible in the test-failure backtrace). Whatever ERT's should-error is doing in 29.4, it isn't accepting the signal as a pass. Replacing should-error with a manual condition-case wrapped in should sidesteps the fragility — we just verify SOMETHING was signalled, which is all the test ever needed. Extracted as a test-scheduler--should-cl-assert helper macro in each file (the two test files don't share infrastructure right now). Locally green; expected to clear the 29.4 CI failure.
* test: pin should-error type to cl-assertion-failed in scheduler error testsCraig Jennings2026-05-052-8/+16
| | | | | | | | | | | | | | | | | | CI on Emacs 29.4 failed eight scheduler :error: tests (5 in Simple8, 3 in SM5). All eight wrap a function call that violates a cl-assert precondition and use bare (should-error ...) to catch the resulting cl-assertion-failed signal. The same tests pass locally (Emacs 30.2) and in CI on Emacs snapshot. Hypothesis: in 29.4 cl-assertion-failed isn't registered with error as a parent class, so the default should-error filter (which catches type 'error') doesn't match. Adding an explicit :type 'cl-assertion-failed' tells should-error exactly what condition to expect, avoiding the inheritance-class question entirely. Locally still green; expected to clear the CI failure on the next push.
* docs: revert intro paragraph; drop personal email mention from historyCraig Jennings2026-05-051-2/+2
| | | | | | | | | | | | Two README fixes: - Restored the original three-sentence intro paragraph. Misread an earlier instruction and removed the wrong second sentence. - Removed the line about emailing phillord and a possible maintainer takeover from the History section. That's a personal outreach detail that doesn't belong in a public README. Rephrased the surrounding paragraph to focus on what's happening in this fork rather than the maintainer-handoff plans.
* docs: tighten README intro and link org-drill.org referencesCraig Jennings2026-05-051-3/+3
| | | | | | | | | Removed the second sentence of the intro paragraph and rephrased the remaining text into a compact two-sentence opener focused on what org-drill is and how scheduling works. Both occurrences of org-drill.org in the README are now relative file: links so a reader on GitHub can click through to the manual.
* ci: add GitHub Actions workflow with test matrix, lint, and coverageCraig Jennings2026-05-052-0/+194
| | | | | | | | | | | | | | | | | | | | | | | | | Three jobs: - test: matrix across Emacs 28.2 / 29.4 / snapshot. Sets up Emacs via jcs090218/setup-emacs and Cask via cask/setup-cask, then runs make setup (with 3 retries to absorb MELPA flakes) and make test-unit. Org 9.6 ships built-in with Emacs 29; on 28 Cask pulls it from MELPA per our depends-on declaration. - lint: Emacs 29.4 only, runs make lint (informational), then make compile and make validate-parens. - coverage: same Emacs version, runs make coverage, prints a per-file summary via scripts/coverage-summary.py (copied from emacs-wttrin), uploads .coverage/simplecov.json as a workflow artifact, and sends results to Coveralls via continue-on-error so CI doesn't fail when COVERALLS_REPO_TOKEN isn't set yet. The README badge URL points at this workflow file (ci.yml) so it auto-populates on the next push to main. Closes the [#B] GitHub Actions TODO. After this lands, the remaining setup is enabling the org-drill repo on coveralls.io and adding COVERALLS_REPO_TOKEN as a GitHub secret so the upload step actually publishes.
* docs: rewrite README as a slim modernized front-doorCraig Jennings2026-05-053-1049/+228
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Replaced the 1049-line README.md (which inlined the entire user manual) with a ~227-line README.org that's the standard 'GitHub front page' for an Emacs package. The deep manual lives where it already does — in org-drill.org, which is also a runnable demo deck. Sections, in order: - Title + nav links - Five badges: made-for-emacs, MELPA, MELPA Stable, GitHub Actions CI, Coveralls. CI and coverage badges are aspirational until the GitHub Actions TODO lands; their URLs are correct so they auto-populate when CI is set up. - Maintenance status: explains the fork, lists recently-fixed upstream issues, points at the GitHub mirror and Issues. - Features: bullet list of capabilities - Installation: MELPA, package-vc-install, use-package :vc, Straight, manual. Drops the stale 'tick drill in org-modules' + Org contrib references. - Quick Start: 60-second flow from install to first drill - In-Session Keys: cheat sheet table for both the question and rating phases (closes the existing #A TODO for the cheat sheet) - Configuration: most-used defcustoms with comments - Development: make-target table - History: contrib → phillord → cjennings handoff story - License pointer Closes three #A TODOs: README modernization, stale install instructions, in-session keybinding cheat sheet. Added assets/made-for-emacs-badge.svg (copied from emacs-wttrin).
* refactor: split org-drill-entry-status classifier from its predicatesCraig Jennings2026-05-051-38/+34
| | | | | | | | | | | | | | | | | Two extractions out of the 58-line cond: - org-drill--entry-empty-and-not-empty-friendly-p: encapsulates the ugly nested predicate that means 'body is empty AND the card type doesn't opt in to empty bodies'. Used to be 5 inlined lines including a let* and an alist lookup. - org-drill--classify-status: takes the precomputed DUE and LAST-INT and walks the cond. The decision tree is now a flat sequence of one-line clauses. org-drill-entry-status itself drops to 8 lines and reads as 'compute the inputs, classify, return triple'. The :failed branch also uses org-drill--quality-failed-p instead of inlining the threshold check.
* refactor: split org-drill-merge-buffers into named phasesCraig Jennings2026-05-051-64/+74
| | | | | | | | | | | | | | | | | | | | | | merge-buffers was 89 lines of dense cross-buffer marker work mixing hash-table population, source-buffer iteration with embedded property reads, scheduling-data writes, and cleanup of unmatched DEST entries. Extracted four helpers: - org-drill--build-dest-id-table: scan DEST and populate the id→ marker table. - org-drill--copy-scheduling-to-marker: read the current entry's scheduling state and write it at MARKER (skipping never-rated items via total-repeats=0 guard). - org-drill--migrate-from-source: walk SRC, dispatch to the copy helper or org-drill-copy-entry-to-other-buffer for new items. - org-drill--strip-unmatched-dest-entries: clean up DEST entries that have no SRC match. merge-buffers itself drops from 89 lines to 19 and reads as a sequence of named phases.
* refactor: split org-drill main entry into named phasesCraig Jennings2026-05-051-99/+103
| | | | | | | | | | | | | | | | | | | | | org-drill was 137 lines mixing five distinct concerns: an org-version warning (now dead under the org>=9.6 floor), session reset, entry collection, drill execution, and post-session messaging. Extracted four helpers, each with a single responsibility: - org-drill--prepare-fresh-session: zero out queues + counters - org-drill--collect-entries: scan + sort overdue - org-drill--queues-empty-p: predicate for the no-pending branch - org-drill--show-end-message: dispatch resume-hint vs final-report Plus org-drill--show-resume-hint for the keystr-aware suspended message. Removed the dead org<7.9.3f warning block (the org>=9.6 floor makes it unreachable). org-drill itself drops from 137 lines to 36 and the cl-block wrapper goes away — the cl-return-from inside org-drill-entries returns through the normal control flow now.
* refactor: split org-drill-final-report into format helpersCraig Jennings2026-05-051-73/+71
| | | | | | | | | | | | | | | | | | | | org-drill-final-report was 95 lines, dominated by two big format calls (the main summary and the low-pass-rate WARNING) plus an inlined queue-tag pattern that propertized 5 different counts in the same shape. Extracted three helpers: - org-drill--queue-tag: builds a propertized 'N label' string for one queue (failed / overdue / new / young / old). Replaces 5 inlined propertize calls. - org-drill--build-final-report-summary: takes the session, and returns the formatted main summary string. - org-drill--build-low-pass-warning: takes the session and pass- percent, returns the formatted warning string. org-drill-final-report becomes a 12-line orchestrator that wires the helpers together with the wait-and-read loop.
* refactor: flatten nesting in org-drill-entriesCraig Jennings2026-05-051-35/+50
| | | | | | | | | | | | | | | | | | | The main loop body reached 7 levels of indentation in the success path: while > destructuring-bind > save-excursion > cond > t-clause > let > cond > inner-cond. Extracted two helpers: - org-drill--pick-next-marker: chooses between resuming the current-item slot and popping a fresh marker, returning a (marker . next-resuming-p) cons so the caller updates resuming-p in lock-step. - org-drill--route-rating-result: routes the rating result into the session's again/done queues and returns a symbol (quit/edit/ skip/next) telling the caller whether to break the loop. org-drill-entries shrinks from 54 lines to 19, and the deepest nesting drops from 7 levels to 4.
* refactor: extract org-drill--read-rating-key shared by reschedule and ↵Craig Jennings2026-05-051-66/+48
| | | | | | | | | | | | | | | | | | | | | | leitner-rebox The two interactive rating loops (reschedule and leitner-rebox) shared roughly 60 lines of identical code: the same key-prompt string, the same memq-based exit-on-rating loop, the same arrow/ scroll/wheel cond, the same tags-key dispatch, and the same help-key toggle. Only two things differ — the rating-explanation help text and the per-rating action that follows — and the comment in leitner-rebox literally said "All this is shared with drill- reschedule. And what does it do?" Extracted org-drill--read-rating-key with two args: typed-answer (for the typed-answer flow's 'Your answer: ...' line) and rating-help-block (the multi-line ratings explanation specific to the scheduler). Both call sites collapse from ~50 lines of inlined loop to a single call. reschedule and leitner-rebox now consist of just the rating- specific actions plus this read-key call.
* refactor: introduce org-drill-with-card-display macroCraig Jennings2026-05-051-60/+60
| | | | | | | | | | | | | | | | | | | Five presenters opened with the same three-deep wrap: (org-drill-with-hidden-comments (org-drill-with-hidden-cloze-hints (org-drill-with-hidden-cloze-text ...body...))) Combined into org-drill-with-card-display. Five sites (present-simple-card, present-simple-card-with-typed-answer, present-two-sided-card, present-multi-sided-card, present-spanish-verb) lose 2-3 lines of nesting each. Multicloze-hide-n / hide-nth use a different envelope (only two of the three wraps; they hide specific clozes by index, not all of them) so they keep their explicit nesting. Same for present-card-using-text, which substitutes with-replaced-entry-text for with-hidden-cloze-text.