contrib/org-drill.el -- updated to version 2.3.2
contrib/org-drill.el -- added to choices for 'org-modules' variable
This commit is contained in:
parent
120e621443
commit
7af369da69
|
@ -1,7 +1,7 @@
|
|||
;;; org-drill.el - Self-testing using spaced repetition
|
||||
;;;
|
||||
;;; Author: Paul Sexton <eeeickythump@gmail.com>
|
||||
;;; Version: 2.3
|
||||
;;; Version: 2.3.2
|
||||
;;; Repository at http://bitbucket.org/eeeickythump/org-drill/
|
||||
;;;
|
||||
;;;
|
||||
|
@ -210,7 +210,11 @@ during a drill session."
|
|||
("hide1cloze" . org-drill-present-multicloze-hide1)
|
||||
("hide2cloze" . org-drill-present-multicloze-hide2)
|
||||
("show1cloze" . org-drill-present-multicloze-show1)
|
||||
("show2cloze" . org-drill-present-multicloze-show2)
|
||||
("multicloze" . org-drill-present-multicloze-hide1)
|
||||
("hide1_firstmore" . org-drill-present-multicloze-hide1-firstmore)
|
||||
("show1_lastmore" . org-drill-present-multicloze-show1-lastmore)
|
||||
("show1_firstless" . org-drill-present-multicloze-show1-firstless)
|
||||
("conjugate" org-drill-present-verb-conjugation
|
||||
org-drill-show-answer-verb-conjugation)
|
||||
("spanish_verb" . org-drill-present-spanish-verb)
|
||||
|
@ -261,6 +265,14 @@ directory All files with the extension '.org' in the same
|
|||
list))
|
||||
|
||||
|
||||
(defcustom org-drill-save-buffers-after-drill-sessions-p
|
||||
t
|
||||
"If non-nil, prompt to save all modified buffers after a drill session
|
||||
finishes."
|
||||
:group 'org-drill
|
||||
:type 'boolean)
|
||||
|
||||
|
||||
(defcustom org-drill-spaced-repetition-algorithm
|
||||
'sm5
|
||||
"Which SuperMemo spaced repetition algorithm to use for scheduling items.
|
||||
|
@ -381,7 +393,6 @@ exponential effect on inter-repetition spacing."
|
|||
(defvar *org-drill-due-entry-count* 0)
|
||||
(defvar *org-drill-overdue-entry-count* 0)
|
||||
(defvar *org-drill-due-tomorrow-count* 0)
|
||||
(defvar *org-drill-current-entry-schedule-type* nil)
|
||||
(defvar *org-drill-overdue-entries* nil
|
||||
"List of markers for items that are considered 'overdue', based on
|
||||
the value of ORG-DRILL-OVERDUE-INTERVAL-FACTOR.")
|
||||
|
@ -431,6 +442,7 @@ for review unless they were already reviewed in the recent past?")
|
|||
(put 'org-drill-overdue-interval-factor 'safe-local-variable 'floatp)
|
||||
(put 'org-drill-scope 'safe-local-variable
|
||||
'(lambda (val) (or (symbolp val) (listp val))))
|
||||
(put 'org-drill-save-buffers-after-drill-sessions-p 'safe-local-variable 'booleanp)
|
||||
|
||||
|
||||
;;;; Utilities ================================================================
|
||||
|
@ -479,6 +491,13 @@ Example: (round-float 3.56755765 3) -> 3.568"
|
|||
(/ (float (round (* floatnum n))) n)))
|
||||
|
||||
|
||||
(defun command-keybinding-to-string (cmd)
|
||||
"Return a human-readable description of the key/keys to which the command
|
||||
CMD is bound, or nil if it is not bound to a key."
|
||||
(let ((key (where-is-internal cmd overriding-local-map t)))
|
||||
(if key (key-description key))))
|
||||
|
||||
|
||||
(defun time-to-inactive-org-timestamp (time)
|
||||
(format-time-string
|
||||
(concat "[" (substring (cdr org-time-stamp-formats) 1 -1) "]")
|
||||
|
@ -1276,6 +1295,7 @@ the current topic."
|
|||
(mature-entry-count (+ (length *org-drill-young-mature-entries*)
|
||||
(length *org-drill-old-mature-entries*)
|
||||
(length *org-drill-overdue-entries*)))
|
||||
(status (first (org-drill-entry-status)))
|
||||
(prompt
|
||||
(if fmt-and-args
|
||||
(apply 'format
|
||||
|
@ -1287,13 +1307,14 @@ the current topic."
|
|||
(format "%s %s %s %s %s %s"
|
||||
(propertize
|
||||
(char-to-string
|
||||
(case *org-drill-current-entry-schedule-type*
|
||||
(new ?N) (young ?Y) (old ?o) (overdue ?!) (failed ?F) (t ??)))
|
||||
(case status
|
||||
(:new ?N) (:young ?Y) (:old ?o) (:overdue ?!)
|
||||
(:failed ?F) (t ??)))
|
||||
'face `(:foreground
|
||||
,(case *org-drill-current-entry-schedule-type*
|
||||
(new org-drill-new-count-color)
|
||||
((young old) org-drill-mature-count-color)
|
||||
((overdue failed) org-drill-failed-count-color)
|
||||
,(case status
|
||||
(:new org-drill-new-count-color)
|
||||
((:young :old) org-drill-mature-count-color)
|
||||
((:overdue :failed) org-drill-failed-count-color)
|
||||
(t org-drill-done-count-color))))
|
||||
(propertize
|
||||
(number-to-string (length *org-drill-done-entries*))
|
||||
|
@ -1547,15 +1568,30 @@ Note: does not actually alter the item."
|
|||
(org-drill-hide-subheadings-if 'org-drill-entry-p)))))))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-hide-n (number-to-hide)
|
||||
(defun org-drill-present-multicloze-hide-n (number-to-hide
|
||||
&optional
|
||||
force-show-first
|
||||
force-show-last
|
||||
force-hide-first)
|
||||
"Hides NUMBER-TO-HIDE pieces of text that are marked for cloze deletion,
|
||||
chosen at random."
|
||||
chosen at random.
|
||||
If NUMBER-TO-HIDE is negative, show only (ABS NUMBER-TO-HIDE) pieces,
|
||||
hiding all the rest.
|
||||
If FORCE-HIDE-FIRST is non-nil, force the first piece of text to be one of
|
||||
the hidden items.
|
||||
If FORCE-SHOW-FIRST is non-nil, never hide the first piece of text.
|
||||
If FORCE-SHOW-LAST is non-nil, never hide the last piece of text.
|
||||
If the number of text pieces in the item is less than
|
||||
NUMBER-TO-HIDE, then all text pieces will be hidden (except the first or last
|
||||
items if FORCE-SHOW-FIRST or FORCE-SHOW-LAST is non-nil)."
|
||||
(with-hidden-comments
|
||||
(with-hidden-cloze-hints
|
||||
(let ((item-end nil)
|
||||
(match-count 0)
|
||||
(body-start (or (cdr (org-get-property-block))
|
||||
(point))))
|
||||
(if (and force-hide-first force-show-first)
|
||||
(error "FORCE-HIDE-FIRST and FORCE-SHOW-FIRST are mutually exclusive"))
|
||||
(org-drill-hide-all-subheadings-except nil)
|
||||
(save-excursion
|
||||
(outline-next-heading)
|
||||
|
@ -1564,10 +1600,24 @@ chosen at random."
|
|||
(goto-char body-start)
|
||||
(while (re-search-forward org-drill-cloze-regexp item-end t)
|
||||
(incf match-count)))
|
||||
(if (minusp number-to-hide)
|
||||
(setq number-to-hide (+ match-count number-to-hide)))
|
||||
(when (plusp match-count)
|
||||
(let ((match-nums (subseq (shuffle-list (loop for i from 1 to match-count
|
||||
collect i))
|
||||
0 number-to-hide)))
|
||||
(let* ((positions (shuffle-list (loop for i from 1
|
||||
to match-count
|
||||
collect i)))
|
||||
(match-nums nil))
|
||||
(if force-hide-first
|
||||
;; Force '1' to be in the list, and to be the first item
|
||||
;; in the list.
|
||||
(setq positions (cons 1 (remove 1 positions))))
|
||||
(if force-show-first
|
||||
(setq positions (remove 1 positions)))
|
||||
(if force-show-last
|
||||
(setq positions (remove match-count positions)))
|
||||
(setq match-nums
|
||||
(subseq positions
|
||||
0 (min number-to-hide (length positions))))
|
||||
(dolist (pos-to-hide match-nums)
|
||||
(save-excursion
|
||||
(goto-char body-start)
|
||||
|
@ -1593,39 +1643,10 @@ chosen at random."
|
|||
(org-drill-present-multicloze-hide-n 2))
|
||||
|
||||
|
||||
;; (defun org-drill-present-multicloze-hide1 ()
|
||||
;; "Hides one of the pieces of text that are marked for cloze deletion,
|
||||
;; chosen at random."
|
||||
;; (with-hidden-comments
|
||||
;; (let ((item-end nil)
|
||||
;; (match-count 0)
|
||||
;; (body-start (or (cdr (org-get-property-block))
|
||||
;; (point))))
|
||||
;; (org-drill-hide-all-subheadings-except nil)
|
||||
;; (save-excursion
|
||||
;; (outline-next-heading)
|
||||
;; (setq item-end (point)))
|
||||
;; (save-excursion
|
||||
;; (goto-char body-start)
|
||||
;; (while (re-search-forward org-drill-cloze-regexp item-end t)
|
||||
;; (incf match-count)))
|
||||
;; (when (plusp match-count)
|
||||
;; (save-excursion
|
||||
;; (goto-char body-start)
|
||||
;; (re-search-forward org-drill-cloze-regexp
|
||||
;; item-end t (1+ (random match-count)))
|
||||
;; (org-drill-hide-matched-cloze-text)))
|
||||
;; (org-display-inline-images t)
|
||||
;; (org-cycle-hide-drawers 'all)
|
||||
;; (prog1 (org-drill-presentation-prompt)
|
||||
;; (org-drill-hide-subheadings-if 'org-drill-entry-p)
|
||||
;; (org-drill-unhide-clozed-text)))))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-show1 ()
|
||||
"Similar to `org-drill-present-multicloze-hide1', but hides all
|
||||
the pieces of text that are marked for cloze deletion, except for one
|
||||
piece which is chosen at random."
|
||||
(defun org-drill-present-multicloze-hide-nth (cnt)
|
||||
"Hide the CNT'th piece of clozed text. 1 is the first piece. If
|
||||
CNT is negative, count backwards, so -1 means the last item, -2
|
||||
the second to last, etc."
|
||||
(with-hidden-comments
|
||||
(with-hidden-cloze-hints
|
||||
(let ((item-end nil)
|
||||
|
@ -1640,15 +1661,17 @@ piece which is chosen at random."
|
|||
(goto-char body-start)
|
||||
(while (re-search-forward org-drill-cloze-regexp item-end t)
|
||||
(incf match-count)))
|
||||
(when (plusp match-count)
|
||||
(let ((match-to-hide (random* match-count)))
|
||||
(save-excursion
|
||||
(goto-char body-start)
|
||||
(dotimes (n match-count)
|
||||
(re-search-forward org-drill-cloze-regexp
|
||||
item-end t)
|
||||
(unless (= n match-to-hide)
|
||||
(org-drill-hide-matched-cloze-text))))))
|
||||
(cond
|
||||
((or (not (plusp match-count))
|
||||
(> cnt match-count)
|
||||
(and (minusp cnt) (> (abs cnt) match-count)))
|
||||
nil)
|
||||
(t
|
||||
(save-excursion
|
||||
(goto-char body-start)
|
||||
(re-search-forward org-drill-cloze-regexp
|
||||
item-end t (if (minusp cnt) (+ 1 cnt match-count) cnt))
|
||||
(org-drill-hide-matched-cloze-text))))
|
||||
(org-display-inline-images t)
|
||||
(org-cycle-hide-drawers 'all)
|
||||
(prog1 (org-drill-presentation-prompt)
|
||||
|
@ -1656,6 +1679,106 @@ piece which is chosen at random."
|
|||
(org-drill-unhide-clozed-text))))))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-hide-first ()
|
||||
"Hides the first piece of text that is marked for cloze deletion."
|
||||
(org-drill-present-multicloze-hide-nth 1))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-hide-last ()
|
||||
"Hides the last piece of text that is marked for cloze deletion."
|
||||
(org-drill-present-multicloze-hide-nth -1))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-hide1-firstmore ()
|
||||
"Three out of every four repetitions, hides the FIRST piece of
|
||||
text that is marked for cloze deletion. One out of every four
|
||||
repetitions, hide one of the other pieces of text, chosen at
|
||||
random."
|
||||
;; The 'firstmore' and 'lastmore' functions used to randomly choose whether
|
||||
;; to hide the 'favoured' piece of text. However even when the chance of
|
||||
;; hiding it was set quite high (80%), the outcome was too unpredictable over
|
||||
;; the small number of repetitions where most learning takes place for each
|
||||
;; item. In other words, the actual frequency during the first 10 repetitions
|
||||
;; was often very different from 80%. Hence we use modulo instead.
|
||||
(if (zerop (mod (1+ (org-drill-entry-total-repeats 0)) 4))
|
||||
;; 25% of time, hide any item except the first
|
||||
(org-drill-present-multicloze-hide-n 1 t)
|
||||
;; 75% of time, hide first item
|
||||
(org-drill-present-multicloze-hide-first)))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-show1-lastmore ()
|
||||
"Three out of every four repetitions, hides all pieces except
|
||||
the last. One out of every four repetitions, shows any random
|
||||
piece. The effect is similar to 'show1cloze' except that the last
|
||||
item is much less likely to be the item that is visible."
|
||||
(if (zerop (mod (1+ (org-drill-entry-total-repeats 0)) 4))
|
||||
;; 25% of time, show any item except the last
|
||||
(org-drill-present-multicloze-hide-n -1 nil t)
|
||||
;; 75% of time, show the LAST item
|
||||
(org-drill-present-multicloze-hide-n -1 nil t)))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-show1-firstless ()
|
||||
"Three out of every four repetitions, hides all pieces except
|
||||
one, where the shown piece is guaranteed NOT to be the first
|
||||
piece. One out of every four repetitions, shows any random
|
||||
piece. The effect is similar to 'show1cloze' except that the
|
||||
first item is much less likely to be the item that is visible."
|
||||
(if (zerop (mod (1+ (org-drill-entry-total-repeats 0)) 4))
|
||||
;; 25% of time, show the first item
|
||||
(org-drill-present-multicloze-hide-n -1 t)
|
||||
;; 75% of time, show any item, except the first
|
||||
(org-drill-present-multicloze-hide-n -1 nil nil t)))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-show1 ()
|
||||
"Similar to `org-drill-present-multicloze-hide1', but hides all
|
||||
the pieces of text that are marked for cloze deletion, except for one
|
||||
piece which is chosen at random."
|
||||
(org-drill-present-multicloze-hide-n -1))
|
||||
|
||||
|
||||
(defun org-drill-present-multicloze-show2 ()
|
||||
"Similar to `org-drill-present-multicloze-show1', but reveals two
|
||||
pieces rather than one."
|
||||
(org-drill-present-multicloze-hide-n -2))
|
||||
|
||||
|
||||
;; (defun org-drill-present-multicloze-show1 ()
|
||||
;; "Similar to `org-drill-present-multicloze-hide1', but hides all
|
||||
;; the pieces of text that are marked for cloze deletion, except for one
|
||||
;; piece which is chosen at random."
|
||||
;; (with-hidden-comments
|
||||
;; (with-hidden-cloze-hints
|
||||
;; (let ((item-end nil)
|
||||
;; (match-count 0)
|
||||
;; (body-start (or (cdr (org-get-property-block))
|
||||
;; (point))))
|
||||
;; (org-drill-hide-all-subheadings-except nil)
|
||||
;; (save-excursion
|
||||
;; (outline-next-heading)
|
||||
;; (setq item-end (point)))
|
||||
;; (save-excursion
|
||||
;; (goto-char body-start)
|
||||
;; (while (re-search-forward org-drill-cloze-regexp item-end t)
|
||||
;; (incf match-count)))
|
||||
;; (when (plusp match-count)
|
||||
;; (let ((match-to-hide (random* match-count)))
|
||||
;; (save-excursion
|
||||
;; (goto-char body-start)
|
||||
;; (dotimes (n match-count)
|
||||
;; (re-search-forward org-drill-cloze-regexp
|
||||
;; item-end t)
|
||||
;; (unless (= n match-to-hide)
|
||||
;; (org-drill-hide-matched-cloze-text))))))
|
||||
;; (org-display-inline-images t)
|
||||
;; (org-cycle-hide-drawers 'all)
|
||||
;; (prog1 (org-drill-presentation-prompt)
|
||||
;; (org-drill-hide-subheadings-if 'org-drill-entry-p)
|
||||
;; (org-drill-unhide-clozed-text))))))
|
||||
|
||||
|
||||
(defun org-drill-present-card-using-text (question &optional answer)
|
||||
"Present the string QUESTION as the only visible content of the card."
|
||||
(with-hidden-comments
|
||||
|
@ -1716,23 +1839,25 @@ See `org-drill' for more details."
|
|||
'org-drill-present-default-answer)
|
||||
presentation-fn (first presentation-fn)))
|
||||
(cond
|
||||
(presentation-fn
|
||||
(setq cont (funcall presentation-fn)))
|
||||
((null presentation-fn)
|
||||
(message "%s:%d: Unrecognised card type '%s', skipping..."
|
||||
(buffer-name) (point) card-type)
|
||||
(sit-for 0.5)
|
||||
'skip)
|
||||
(t
|
||||
(error "Unknown card type: '%s'" card-type))))
|
||||
|
||||
(cond
|
||||
((not cont)
|
||||
(message "Quit")
|
||||
nil)
|
||||
((eql cont 'edit)
|
||||
'edit)
|
||||
((eql cont 'skip)
|
||||
'skip)
|
||||
(t
|
||||
(save-excursion
|
||||
(funcall answer-fn
|
||||
(lambda () (org-drill-reschedule))))))))))
|
||||
(setq cont (funcall presentation-fn))
|
||||
(cond
|
||||
((not cont)
|
||||
(message "Quit")
|
||||
nil)
|
||||
((eql cont 'edit)
|
||||
'edit)
|
||||
((eql cont 'skip)
|
||||
'skip)
|
||||
(t
|
||||
(save-excursion
|
||||
(funcall answer-fn
|
||||
(lambda () (org-drill-reschedule)))))))))))))
|
||||
|
||||
|
||||
(defun org-drill-entries-pending-p ()
|
||||
|
@ -1785,7 +1910,6 @@ maximum number of items."
|
|||
((and *org-drill-failed-entries*
|
||||
(not (org-drill-maximum-item-count-reached-p))
|
||||
(not (org-drill-maximum-duration-reached-p)))
|
||||
(setq *org-drill-current-entry-schedule-type* 'failed)
|
||||
(pop-random *org-drill-failed-entries*))
|
||||
;; Next priority is overdue items.
|
||||
((and *org-drill-overdue-entries*
|
||||
|
@ -1794,13 +1918,11 @@ maximum number of items."
|
|||
;; We use `pop', not `pop-random', because we have already
|
||||
;; sorted overdue items into a random order which takes
|
||||
;; number of days overdue into account.
|
||||
(setq *org-drill-current-entry-schedule-type* 'overdue)
|
||||
(pop *org-drill-overdue-entries*))
|
||||
;; Next priority is 'young' items.
|
||||
((and *org-drill-young-mature-entries*
|
||||
(not (org-drill-maximum-item-count-reached-p))
|
||||
(not (org-drill-maximum-duration-reached-p)))
|
||||
(setq *org-drill-current-entry-schedule-type* 'young)
|
||||
(pop-random *org-drill-young-mature-entries*))
|
||||
;; Next priority is newly added items, and older entries.
|
||||
;; We pool these into a single group.
|
||||
|
@ -1812,15 +1934,12 @@ maximum number of items."
|
|||
((< (random* (+ (length *org-drill-new-entries*)
|
||||
(length *org-drill-old-mature-entries*)))
|
||||
(length *org-drill-new-entries*))
|
||||
(setq *org-drill-current-entry-schedule-type* 'new)
|
||||
(pop-random *org-drill-new-entries*))
|
||||
(t
|
||||
(setq *org-drill-current-entry-schedule-type* 'old)
|
||||
(pop-random *org-drill-old-mature-entries*))))
|
||||
;; After all the above are done, last priority is items
|
||||
;; that were failed earlier THIS SESSION.
|
||||
(*org-drill-again-entries*
|
||||
(setq *org-drill-current-entry-schedule-type* 'failed)
|
||||
(pop *org-drill-again-entries*))
|
||||
(t ; nothing left -- return nil
|
||||
(return-from org-drill-pop-next-pending-entry nil)))))
|
||||
|
@ -1847,26 +1966,35 @@ RESUMING-P is true if we are resuming a suspended drill session."
|
|||
(error "Unexpectedly ran out of pending drill items"))
|
||||
(save-excursion
|
||||
(org-drill-goto-entry m)
|
||||
(setq result (org-drill-entry))
|
||||
(cond
|
||||
((null result)
|
||||
(message "Quit")
|
||||
(setq end-pos :quit)
|
||||
(return-from org-drill-entries nil))
|
||||
((eql result 'edit)
|
||||
(setq end-pos (point-marker))
|
||||
(return-from org-drill-entries nil))
|
||||
((eql result 'skip)
|
||||
nil) ; skip this item
|
||||
((not (org-drill-entry-due-p))
|
||||
;; The entry is not due anymore. This could arise if the user
|
||||
;; suspends a drill session, then drills an individual entry,
|
||||
;; then resumes the session.
|
||||
(message "Entry no longer due, skipping...")
|
||||
(sit-for 0.3)
|
||||
nil)
|
||||
(t
|
||||
(setq result (org-drill-entry))
|
||||
(cond
|
||||
((<= result org-drill-failure-quality)
|
||||
(if *org-drill-again-entries*
|
||||
(setq *org-drill-again-entries*
|
||||
(shuffle-list *org-drill-again-entries*)))
|
||||
(push-end m *org-drill-again-entries*))
|
||||
((null result)
|
||||
(message "Quit")
|
||||
(setq end-pos :quit)
|
||||
(return-from org-drill-entries nil))
|
||||
((eql result 'edit)
|
||||
(setq end-pos (point-marker))
|
||||
(return-from org-drill-entries nil))
|
||||
((eql result 'skip)
|
||||
nil) ; skip this item
|
||||
(t
|
||||
(push m *org-drill-done-entries*))))))))))
|
||||
(cond
|
||||
((<= result org-drill-failure-quality)
|
||||
(if *org-drill-again-entries*
|
||||
(setq *org-drill-again-entries*
|
||||
(shuffle-list *org-drill-again-entries*)))
|
||||
(push-end m *org-drill-again-entries*))
|
||||
(t
|
||||
(push m *org-drill-done-entries*))))))))))))
|
||||
|
||||
|
||||
|
||||
|
@ -1961,14 +2089,19 @@ order to make items appear more frequently over time."
|
|||
|
||||
|
||||
|
||||
(defun org-drill-free-all-markers ()
|
||||
(dolist (m (append *org-drill-done-entries*
|
||||
*org-drill-new-entries*
|
||||
*org-drill-failed-entries*
|
||||
*org-drill-again-entries*
|
||||
*org-drill-overdue-entries*
|
||||
*org-drill-young-mature-entries*
|
||||
*org-drill-old-mature-entries*))
|
||||
(defun org-drill-free-markers (markers)
|
||||
"MARKERS is a list of markers, all of which will be freed (set to
|
||||
point nowhere). Alternatively, MARKERS can be 't', in which case
|
||||
all the markers used by Org-Drill will be freed."
|
||||
(dolist (m (if (eql t markers)
|
||||
(append *org-drill-done-entries*
|
||||
*org-drill-new-entries*
|
||||
*org-drill-failed-entries*
|
||||
*org-drill-again-entries*
|
||||
*org-drill-overdue-entries*
|
||||
*org-drill-young-mature-entries*
|
||||
*org-drill-old-mature-entries*)
|
||||
markers))
|
||||
(free-marker m)))
|
||||
|
||||
|
||||
|
@ -1979,6 +2112,58 @@ order to make items appear more frequently over time."
|
|||
(lambda (a b) (> (cdr a) (cdr b)))))))
|
||||
|
||||
|
||||
(defun org-drill-entry-status ()
|
||||
"Returns a list (STATUS DUE) where DUE is the number of days overdue,
|
||||
zero being due today, -1 being scheduled 1 day in the future. STATUS is
|
||||
one of the following values:
|
||||
- nil, if the item is not a drill entry, or has an empty body
|
||||
- :unscheduled
|
||||
- :future
|
||||
- :new
|
||||
- :failed
|
||||
- :overdue
|
||||
- :young
|
||||
- :old
|
||||
"
|
||||
(save-excursion
|
||||
(unless (org-at-heading-p)
|
||||
(org-back-to-heading))
|
||||
(let ((due (org-drill-entry-days-overdue))
|
||||
(last-int (org-drill-entry-last-interval 1)))
|
||||
(list
|
||||
(cond
|
||||
((not (org-drill-entry-p))
|
||||
nil)
|
||||
((org-drill-entry-empty-p)
|
||||
nil) ; skip -- item body is empty
|
||||
((null due) ; unscheduled - usually a skipped leech
|
||||
:unscheduled)
|
||||
;; ((eql -1 due)
|
||||
;; :tomorrow)
|
||||
((minusp due) ; scheduled in the future
|
||||
:future)
|
||||
;; The rest of the stati all denote 'due' items ==========================
|
||||
((<= (org-drill-entry-last-quality 9999)
|
||||
org-drill-failure-quality)
|
||||
;; Mature entries that were failed last time are
|
||||
;; FAILED, regardless of how young, old or overdue
|
||||
;; they are.
|
||||
:failed)
|
||||
((org-drill-entry-new-p)
|
||||
:new)
|
||||
((org-drill-entry-overdue-p due last-int)
|
||||
;; Overdue status overrides young versus old
|
||||
;; distinction.
|
||||
;; Store marker + due, for sorting of overdue entries
|
||||
:overdue)
|
||||
((<= (org-drill-entry-last-interval 9999)
|
||||
org-drill-days-before-old)
|
||||
:young)
|
||||
(t
|
||||
:old))
|
||||
due))))
|
||||
|
||||
|
||||
(defun org-drill (&optional scope resume-p)
|
||||
"Begin an interactive 'drill session'. The user is asked to
|
||||
review a series of topics (headers). Each topic is initially
|
||||
|
@ -2016,7 +2201,7 @@ than starting a new one."
|
|||
(cnt 0))
|
||||
(block org-drill
|
||||
(unless resume-p
|
||||
(org-drill-free-all-markers)
|
||||
(org-drill-free-markers t)
|
||||
(setq *org-drill-current-item* nil
|
||||
*org-drill-done-entries* nil
|
||||
*org-drill-dormant-entry-count* 0
|
||||
|
@ -2058,38 +2243,59 @@ than starting a new one."
|
|||
(sit-for 0.5)
|
||||
(setq warned-about-id-creation t))
|
||||
(org-id-get-create) ; ensure drill entry has unique ID
|
||||
(let ((due (org-drill-entry-days-overdue))
|
||||
(last-int (org-drill-entry-last-interval 1)))
|
||||
(cond
|
||||
((org-drill-entry-empty-p)
|
||||
nil) ; skip -- item body is empty
|
||||
((or (null due) ; unscheduled - usually a skipped leech
|
||||
(minusp due)) ; scheduled in the future
|
||||
(incf *org-drill-dormant-entry-count*)
|
||||
(if (eq -1 due)
|
||||
(incf *org-drill-due-tomorrow-count*)))
|
||||
((org-drill-entry-new-p)
|
||||
(push (point-marker) *org-drill-new-entries*))
|
||||
((<= (org-drill-entry-last-quality 9999)
|
||||
org-drill-failure-quality)
|
||||
;; Mature entries that were failed last time are
|
||||
;; FAILED, regardless of how young, old or overdue
|
||||
;; they are.
|
||||
(push (point-marker) *org-drill-failed-entries*))
|
||||
((org-drill-entry-overdue-p due last-int)
|
||||
;; Overdue status overrides young versus old
|
||||
;; distinction.
|
||||
;; Store marker + due, for sorting of overdue entries
|
||||
(push (cons (point-marker) due) overdue-data))
|
||||
((<= (org-drill-entry-last-interval 9999)
|
||||
org-drill-days-before-old)
|
||||
;; Item is 'young'.
|
||||
(push (point-marker)
|
||||
*org-drill-young-mature-entries*))
|
||||
(t
|
||||
(push (point-marker)
|
||||
*org-drill-old-mature-entries*)))))))
|
||||
(destructuring-bind (status due) (org-drill-entry-status)
|
||||
(case status
|
||||
(:unscheduled
|
||||
(incf *org-drill-dormant-entry-count*))
|
||||
;; (:tomorrow
|
||||
;; (incf *org-drill-dormant-entry-count*)
|
||||
;; (incf *org-drill-due-tomorrow-count*))
|
||||
(:future
|
||||
(incf *org-drill-dormant-entry-count*)
|
||||
(if (eq -1 due)
|
||||
(incf *org-drill-due-tomorrow-count*)))
|
||||
(:new
|
||||
(push (point-marker) *org-drill-new-entries*))
|
||||
(:failed
|
||||
(push (point-marker) *org-drill-failed-entries*))
|
||||
(:young
|
||||
(push (point-marker) *org-drill-young-mature-entries*))
|
||||
(:overdue
|
||||
(push (cons (point-marker) due) overdue-data))
|
||||
(:old
|
||||
(push (point-marker) *org-drill-old-mature-entries*)))))))
|
||||
scope)
|
||||
;; (let ((due (org-drill-entry-days-overdue))
|
||||
;; (last-int (org-drill-entry-last-interval 1)))
|
||||
;; (cond
|
||||
;; ((org-drill-entry-empty-p)
|
||||
;; nil) ; skip -- item body is empty
|
||||
;; ((or (null due) ; unscheduled - usually a skipped leech
|
||||
;; (minusp due)) ; scheduled in the future
|
||||
;; (incf *org-drill-dormant-entry-count*)
|
||||
;; (if (eq -1 due)
|
||||
;; (incf *org-drill-due-tomorrow-count*)))
|
||||
;; ((org-drill-entry-new-p)
|
||||
;; (push (point-marker) *org-drill-new-entries*))
|
||||
;; ((<= (org-drill-entry-last-quality 9999)
|
||||
;; org-drill-failure-quality)
|
||||
;; ;; Mature entries that were failed last time are
|
||||
;; ;; FAILED, regardless of how young, old or overdue
|
||||
;; ;; they are.
|
||||
;; (push (point-marker) *org-drill-failed-entries*))
|
||||
;; ((org-drill-entry-overdue-p due last-int)
|
||||
;; ;; Overdue status overrides young versus old
|
||||
;; ;; distinction.
|
||||
;; ;; Store marker + due, for sorting of overdue entries
|
||||
;; (push (cons (point-marker) due) overdue-data))
|
||||
;; ((<= (org-drill-entry-last-interval 9999)
|
||||
;; org-drill-days-before-old)
|
||||
;; ;; Item is 'young'.
|
||||
;; (push (point-marker)
|
||||
;; *org-drill-young-mature-entries*))
|
||||
;; (t
|
||||
;; (push (point-marker)
|
||||
;; *org-drill-old-mature-entries*))))
|
||||
;; Order 'overdue' items so that the most overdue will tend to
|
||||
;; come up for review first, while keeping exact order random
|
||||
(org-drill-order-overdue-entries overdue-data)
|
||||
|
@ -2108,21 +2314,25 @@ than starting a new one."
|
|||
(message "Drill session finished!"))))
|
||||
(progn
|
||||
(unless end-pos
|
||||
(org-drill-free-all-markers)))))
|
||||
(org-drill-free-markers *org-drill-done-entries*)))))
|
||||
(cond
|
||||
(end-pos
|
||||
(when (markerp end-pos)
|
||||
(org-drill-goto-entry end-pos))
|
||||
(message
|
||||
"You can continue the drill session with `M-x org-drill-resume'."))
|
||||
(let ((keystr (command-keybinding-to-string 'org-drill-resume)))
|
||||
(message
|
||||
"You can continue the drill session with the command `org-drill-resume'.%s"
|
||||
(if keystr (format "\nYou can run this command by pressing %s." keystr)
|
||||
""))))
|
||||
(t
|
||||
(org-drill-final-report)
|
||||
(if (eql 'sm5 org-drill-spaced-repetition-algorithm)
|
||||
(org-drill-save-optimal-factor-matrix))
|
||||
(if org-drill-save-buffers-after-drill-sessions-p
|
||||
(save-some-buffers))
|
||||
))))
|
||||
|
||||
|
||||
|
||||
(defun org-drill-save-optimal-factor-matrix ()
|
||||
(message "Saving optimal factor matrix...")
|
||||
(customize-save-variable 'org-drill-optimal-factor-matrix
|
||||
|
@ -2153,11 +2363,43 @@ files in the same directory as the current file."
|
|||
(org-drill 'directory))
|
||||
|
||||
|
||||
(defun org-drill-again (&optional scope)
|
||||
"Run a new drill session, but try to use leftover due items that
|
||||
were not reviewed during the last session, rather than scanning for
|
||||
unreviewed items. If there are no leftover items in memory, a full
|
||||
scan will be performed."
|
||||
(interactive)
|
||||
(cond
|
||||
((plusp (org-drill-pending-entry-count))
|
||||
(org-drill-free-markers *org-drill-done-entries*)
|
||||
(if (markerp *org-drill-current-item*)
|
||||
(free-marker *org-drill-current-item*))
|
||||
(setq *org-drill-start-time* (float-time (current-time))
|
||||
*org-drill-done-entries* nil
|
||||
*org-drill-current-item* nil)
|
||||
(org-drill scope t))
|
||||
(t
|
||||
(org-drill scope))))
|
||||
|
||||
|
||||
|
||||
(defun org-drill-resume ()
|
||||
"Resume a suspended drill session. Sessions are suspended by
|
||||
exiting them with the `edit' option."
|
||||
exiting them with the `edit' or `quit' options."
|
||||
(interactive)
|
||||
(org-drill nil t))
|
||||
(cond
|
||||
((org-drill-entries-pending-p)
|
||||
(org-drill nil t))
|
||||
((and (plusp (org-drill-pending-entry-count))
|
||||
;; Current drill session is finished, but there are still
|
||||
;; more items which need to be reviewed.
|
||||
(y-or-n-p (format
|
||||
"You have finished the drill session. However, %d items still
|
||||
need reviewing. Start a new drill session? "
|
||||
(org-drill-pending-entry-count))))
|
||||
(org-drill-again))
|
||||
(t
|
||||
(message "You have finished the drill session."))))
|
||||
|
||||
|
||||
(defun org-drill-strip-entry-data ()
|
||||
|
@ -2249,13 +2491,20 @@ the tag 'imported'."
|
|||
|
||||
|
||||
|
||||
(defun org-drill-merge-buffers (src &optional dest)
|
||||
(defun org-drill-merge-buffers (src &optional dest ignore-new-items-p)
|
||||
"SRC and DEST are two org mode buffers containing drill items.
|
||||
For each drill item in DEST that shares an ID with an item in SRC,
|
||||
overwrite scheduling data in DEST with data taken from the item in SRC.
|
||||
This is intended for use when two people are sharing a set of drill items,
|
||||
one person has made some updates to the item set, and the other person
|
||||
wants to migrate to the updated set without losing their scheduling data."
|
||||
wants to migrate to the updated set without losing their scheduling data.
|
||||
|
||||
By default, any drill items in SRC which do not exist in DEST are
|
||||
copied into DEST. We attempt to place the copied item in the
|
||||
equivalent location in DEST to its location in SRC, by matching
|
||||
the heading hierarchy. However if IGNORE-NEW-ITEMS-P is non-nil,
|
||||
we simply ignore any items that do not exist in DEST, and do not
|
||||
copy them across."
|
||||
;; In future could look at what to do if we find an item in SRC whose ID
|
||||
;; is not present in DEST -- copy the whole item to DEST?
|
||||
;; org-copy-subtree --> org-paste-subtree
|
||||
|
@ -2309,17 +2558,32 @@ wants to migrate to the updated set without losing their scheduling data."
|
|||
(unless (zerop total-repeats)
|
||||
(org-drill-store-item-data last-interval repetitions failures
|
||||
total-repeats meanq ease)
|
||||
(org-set-property "LAST_QUALITY" last-quality)
|
||||
(org-set-property "LAST_REVIEWED" last-reviewed)
|
||||
(if last-quality
|
||||
(org-set-property "LAST_QUALITY" last-quality)
|
||||
(org-delete-property "LAST_QUALITY"))
|
||||
(if last-reviewed
|
||||
(org-set-property "LAST_REVIEWED" last-reviewed)
|
||||
(org-delete-property "LAST_REVIEWED"))
|
||||
(if scheduled-time
|
||||
(org-schedule nil scheduled-time)))))
|
||||
(remhash id *org-drill-dest-id-table*)
|
||||
(free-marker marker)))
|
||||
(t
|
||||
;; item in SRC has ID, but no matching ID in DEST.
|
||||
;; It must be a new item that does not exist in DEST.
|
||||
;; Copy the entire item to the *end* of DEST.
|
||||
(org-drill-copy-entry-to-other-buffer dest)))))
|
||||
'file))))
|
||||
(unless ignore-new-items-p
|
||||
(org-drill-copy-entry-to-other-buffer dest))))))
|
||||
'file))
|
||||
;; Finally: there may be some items in DEST which are not in SRC, and
|
||||
;; which have been scheduled by another user of DEST. Clear out the
|
||||
;; scheduling info from all the unmatched items in DEST.
|
||||
(with-current-buffer dest
|
||||
(maphash (lambda (id m)
|
||||
(goto-char m)
|
||||
(org-drill-strip-entry-data)
|
||||
(free-marker m))
|
||||
*org-drill-dest-id-table*))))
|
||||
|
||||
|
||||
|
||||
|
@ -2357,6 +2621,7 @@ the name of the tense.")
|
|||
"Auxiliary function used by `org-drill-present-verb-conjugation' and
|
||||
`org-drill-show-answer-verb-conjugation'."
|
||||
(let ((infinitive (org-entry-get (point) "VERB_INFINITIVE" t))
|
||||
(inf-hint (org-entry-get (point) "VERB_INFINITIVE_HINT" t))
|
||||
(translation (org-entry-get (point) "VERB_TRANSLATION" t))
|
||||
(tense (org-entry-get (point) "VERB_TENSE" nil))
|
||||
(highlight-face nil))
|
||||
|
@ -2365,6 +2630,7 @@ the name of the tense.")
|
|||
infinitive translation tense (point)))
|
||||
(setq tense (downcase (car (read-from-string tense)))
|
||||
infinitive (car (read-from-string infinitive))
|
||||
inf-hint (if inf-hint (car (read-from-string inf-hint)))
|
||||
translation (car (read-from-string translation)))
|
||||
(setq highlight-face
|
||||
(list :foreground
|
||||
|
@ -2373,12 +2639,12 @@ the name of the tense.")
|
|||
(setq infinitive (propertize infinitive 'face highlight-face))
|
||||
(setq translation (propertize translation 'face highlight-face))
|
||||
(setq tense (propertize tense 'face highlight-face))
|
||||
(list infinitive translation tense)))
|
||||
(list infinitive inf-hint translation tense)))
|
||||
|
||||
|
||||
(defun org-drill-present-verb-conjugation ()
|
||||
"Present a drill entry whose card type is 'conjugate'."
|
||||
(destructuring-bind (infinitive translation tense)
|
||||
(destructuring-bind (infinitive inf-hint translation tense)
|
||||
(org-drill-get-verb-conjugation-info)
|
||||
(org-drill-present-card-using-text
|
||||
(cond
|
||||
|
@ -2386,15 +2652,18 @@ the name of the tense.")
|
|||
(format "\nTranslate the verb\n\n%s\n\nand conjugate for the %s tense.\n\n"
|
||||
infinitive tense))
|
||||
(t
|
||||
(format "\nGive the verb that means\n\n%s\n\nand conjugate for the %s tense.\n\n"
|
||||
translation tense))))))
|
||||
(format "\nGive the verb that means\n\n%s %s\n
|
||||
and conjugate for the %s tense.\n\n"
|
||||
translation
|
||||
(if inf-hint (format " [HINT: %s]" inf-hint) "")
|
||||
tense))))))
|
||||
|
||||
|
||||
(defun org-drill-show-answer-verb-conjugation (reschedule-fn)
|
||||
"Show the answer for a drill item whose card type is 'conjugate'.
|
||||
RESCHEDULE-FN must be a function that calls `org-drill-reschedule' and
|
||||
returns its return value."
|
||||
(destructuring-bind (infinitive translation tense)
|
||||
(destructuring-bind (infinitive inf-hint translation tense)
|
||||
(org-drill-get-verb-conjugation-info)
|
||||
(with-replaced-entry-heading
|
||||
(format "%s tense of %s ==> %s\n\n"
|
||||
|
|
|
@ -313,6 +313,7 @@ to add the symbol `xyz', and the package must have a call to
|
|||
(const :tag "C choose: Use TODO keywords to mark decisions states" org-choose)
|
||||
(const :tag "C collector: Collect properties into tables" org-collector)
|
||||
(const :tag "C depend: TODO dependencies for Org-mode\n\t\t\t(PARTIALLY OBSOLETE, see built-in dependency support))" org-depend)
|
||||
(const :tag "C drill: Flashcards and spaced repetition for Org-mode" org-drill)
|
||||
(const :tag "C elisp-symbol: Org-mode links to emacs-lisp symbols" org-elisp-symbol)
|
||||
(const :tag "C eshell Support for links to working directories in eshell" org-eshell)
|
||||
(const :tag "C eval: Include command output as text" org-eval)
|
||||
|
@ -12448,7 +12449,7 @@ only lines with a TODO keyword are included in the output."
|
|||
(progn
|
||||
(unless (eq action 'sparse-tree) (org-agenda-skip))
|
||||
t)
|
||||
|
||||
|
||||
;; Check if timestamps are deselecting this entry
|
||||
(or (not todo-only)
|
||||
(and (member todo org-not-done-keywords)
|
||||
|
|
Loading…
Reference in New Issue