1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
;;; org-drill-config.el --- Org Drill Settings -*- lexical-binding: t; coding: utf-8; -*-
;; author: Craig Jennings <c@cjennings.net>
;;; Commentary:
;;
;; Layer: 4 (Optional).
;; Category: O/D/P.
;; Load shape: eager.
;; Eager reason: none; optional flashcard workflow, a command-loaded deferral
;; candidate for Phase 4.
;; Top-level side effects: defines a drill keymap, registers it under cj/custom-keymap.
;; Runtime requires: user-constants, keybindings.
;; Direct test load: yes (requires keybindings explicitly).
;;
;; Notes: Org-Drill
;; `C-; D s' picks a flashcard file from `drill-dir' and starts a session;
;; `C-u C-; D s' lets you pick the directory first. `C-; D f' drills
;; whatever Org file is current, so any drill file anywhere works.
;; Capture templates:
;; - "d" for web/EPUB drill captures (uses %i for selected text, %:link for source)
;; - "f" for PDF drill captures (uses special PDF region extraction)
;; The javascript bookmarklet for capturing from web to org-drill:
;; javascript:location.href='org-protocol://capture?template=d&url=%27+encodeURIComponent(location.href)+%27&title=%27+encodeURIComponent(document.title)+%27&body=%27+encodeURIComponent(window.getSelection());void(0);
;; Create a bookmark, add "Drill Entry" to the name field and the above snippet to the URL field.
;;; Code:
(require 'user-constants) ;; `drill-dir'
(require 'keybindings) ;; provides `cj/custom-keymap'
(declare-function org-drill "org-drill" (&optional scope drill-match resume-p))
(declare-function org-drill-resume "org-drill" ())
(declare-function org-capture "org-capture" (&optional goto keys))
(declare-function org-refile "org-refile" (&optional arg1 default-buffer rfloc msg))
(defvar org-refile-targets) ;; org-refile.el
;; ------------------------------ Drill Commands -------------------------------
(defun cj/--drill-files-in (dir)
"Return the drill Org file names directly inside DIR (no leading dots)."
(directory-files dir nil "^[^.].*\\.org$"))
(defun cj/--drill-files-or-error (dir)
"Return the drill Org files in DIR, or signal a clear `user-error'.
Errors when DIR is missing, unreadable, or has no drill files, so callers
fail with an actionable message instead of a low-level `directory-files'
error or an empty `completing-read'. This is the single entry point the
drill commands and the drill capture templates share."
(unless (and (file-directory-p dir) (file-readable-p dir))
(user-error "Drill directory missing or unreadable: %s" dir))
(let ((files (cj/--drill-files-in dir)))
(unless files
(user-error "No drill files (.org) found in %s" dir))
files))
(defun cj/--drill-pick-file (dir)
"Prompt for one of the drill Org files in DIR; return its absolute path."
(expand-file-name
(completing-read "Choose flashcard file: " (cj/--drill-files-or-error dir) nil t)
dir))
(defun cj/--drill-pick-dir (other-dir)
"Return the directory to pick drill files from.
With OTHER-DIR non-nil, prompt for one; otherwise use `drill-dir'."
(if other-dir (read-directory-name "Drill files in: ") drill-dir))
(defun cj/drill-start (&optional other-dir)
"Pick a drill Org file and start an `org-drill' session.
With a prefix arg OTHER-DIR, prompt for the directory to choose from
instead of the default `drill-dir'."
(interactive "P")
(find-file (cj/--drill-pick-file (cj/--drill-pick-dir other-dir)))
(org-drill))
(defun cj/drill-this-file ()
"Start an `org-drill' session on the current Org buffer.
Use this to drill any drill file you have open, wherever it lives."
(interactive)
(unless (derived-mode-p 'org-mode)
(user-error "Not an Org buffer -- visit a `.org' file first"))
(org-drill))
(defun cj/drill-edit (&optional other-dir)
"Pick a drill Org file and open it for editing.
With a prefix arg OTHER-DIR, prompt for the directory instead of `drill-dir'."
(interactive "P")
(find-file (cj/--drill-pick-file (cj/--drill-pick-dir other-dir))))
(defun cj/drill-capture ()
"Quickly capture a drill question."
(interactive)
(org-capture nil "d"))
(defun cj/drill-refile ()
"Refile to a drill file."
(interactive)
(setq org-refile-targets '((nil :maxlevel . 1)
(drill-dir :maxlevel . 1)))
(call-interactively 'org-refile))
;; ------------------------------- Drill Keymap --------------------------------
(defvar-keymap cj/drill-map
:doc "Keymap for org-drill"
"s" #'cj/drill-start
"f" #'cj/drill-this-file
"e" #'cj/drill-edit
"c" #'cj/drill-capture
"r" #'cj/drill-refile
"R" #'org-drill-resume)
(cj/register-prefix-map "D" cj/drill-map)
(with-eval-after-load 'which-key
(which-key-add-key-based-replacements
"C-; D" "org-drill menu"
"C-; D s" "start drill (C-u: pick dir)"
"C-; D f" "drill current file"
"C-; D e" "edit drill file (C-u: pick dir)"
"C-; D c" "capture question"
"C-; D r" "refile to drill"
"C-; D R" "resume drill"))
;; --------------------------------- Org Drill ---------------------------------
(use-package org-drill
;; :vc (:url "git@cjennings.net:org-drill.git"
;; :branch "main"
;; :rev :newest)
:load-path "~/code/org-drill" ;; local dev checkout — switch back to :vc above when done
:after (org org-capture)
:demand t
:commands (org-drill org-drill-resume)
:custom
(org-drill-leech-failure-threshold 50 "leech cards = 50 wrong answers")
(org-drill-leech-method 'warn "leech cards show warnings")
(org-drill-use-visible-cloze-face-p t "cloze text shows up in a different font")
(org-drill-hide-item-headings-p t "don't show heading text")
(org-drill-maximum-items-per-session 100 "drill sessions end after 100 cards")
(org-drill-maximum-duration 30 "each drill session can last up to 30 mins")
(org-drill-add-random-noise-to-intervals-p t "vary the days to repetition slightly")
(org-drill-text-size-during-session 24 "24-point font for comfortable reading")
(org-drill-use-variable-pitch t "variable-pitch font for readability")
(org-drill-hide-modeline-during-session t "hide the modeline for a cleaner display"))
(provide 'org-drill-config)
;;; org-drill-config.el ends here.
|