(defvar *flashcard-words*)
(defvar *flashcard-next*)
(defun flashcard-shuffle ()
(let ((n (length *flashcard-words*)))
(dotimes (i (1- n))
(let* ((index (+ i 1 (random (- n i 1))))
(other (aref *flashcard-words* index)))
(aset *flashcard-words* index (aref *flashcard-words* i))
(aset *flashcard-words* i other))))
(setq *flashcard-next* 0))
(defun flashcard-count-matches (regexp rstart rend)
"For compliance with version < 22 emacsen."
(let ((count 0))
(save-excursion
(goto-char rstart)
(while (search-forward-regexp regexp rend t)
(setq count (1+ count))))
count))
(defun flashcard-init ()
(random t)
(let ((count (flashcard-count-matches ".* = .*" (point-min) (point-max))))
(when (= count 0)
(error "There are no usable word pairs in this buffer"))
(setq *flashcard-words* (make-vector count nil))
(save-excursion
(goto-char (point-min))
(dotimes (i count)
(search-forward-regexp "\\(.*\\) = \\(.*\\)")
(aset *flashcard-words* i (list (match-string 1) (match-string 2)))))
(flashcard-shuffle)))
(defun flashcard-next ()
(when (>= *flashcard-next* (length *flashcard-words*))
(flashcard-shuffle))
(prog1 (aref *flashcard-words* *flashcard-next*)
(setq *flashcard-next* (1+ *flashcard-next*))))
(defun flashcard-help (&optional message)
(erase-buffer)
(insert (format "
Welcome to Emacs Flashcards!
There are %d words in the database.
The definitions appear in the minibuffer one by one.
Pressing a key will show the correct answer.
After the answer is displayed you can press
- `r' to remove this word (i.e. it won't be asked again)
- any other key to show the next definition
Press q any time to quit."
(length *flashcard-words*)))
(when message
(insert (format "\n\n=> %s" message))))
(defun flashcard-write-remaining ()
(erase-buffer)
(insert "\nThese words remained:\n\n")
(dotimes (i (length *flashcard-words*))
(let ((word (aref *flashcard-words* i)))
(insert (format "%s = %s\n" (car word) (cadr word))))))
(defun flashcard ()
"Display flashcards in the minibuffer.
Emacs Flashcards searches the current buffer for lines of the format
`solution = definition' and tests the user if he remembers them."
(interactive)
(flashcard-init)
(switch-to-buffer "*flashcard*")
(setq cursor-type nil)
(flashcard-help)
(message "Press q to quit, and any other key to advance.")
(let ((status 'start)
(word nil))
(while (not (eq status 'end))
(let ((c (read-char-exclusive)))
(cond ((char-equal c ?q)
(setq status 'end))
((eq status 'ask)
(message (concat (cadr word) " = " (car word)))
(setq status 'show))
((member status '(start show))
(when (and (not (eq status 'start)) (char-equal c ?r))
(setq *flashcard-words* (remove word *flashcard-words*)
*flashcard-next* (1- *flashcard-next*))
(flashcard-help (format "Word `%s' (%s) removed."
(cadr word) (car word))))
(cond ((= (length *flashcard-words*) 0)
(setq status 'end)
(message "No more words left. Press a key.")
(read-char-exclusive))
(t
(setq word (flashcard-next))
(message (cadr word))
(setq status 'ask))))))))
(if (zerop (length *flashcard-words*))
(kill-buffer "*flashcard*")
(flashcard-write-remaining)
(setq cursor-type t))
(message "Bye!"))