(defvar *flashcard-words*)
(defvar *flashcard-next*)
(defvar *flashcard-state*)
(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))
(setq *flashcard-next* (1+ *flashcard-next*)))
(defun flashcard-help ()
(erase-buffer)
(insert (format "
Welcome to Emacs Flashcards!
The definitions appear in the buffer one by one.
Pressing space 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)
- `n' to show the next definition
Press q any time to quit (the list of remaining words will be displayed).
There are %d words in the database.
Last removed word: N/A
=> " (length *flashcard-words*))))
(defun flashcard-show-removal (str)
"Returns T if there are still words in the database."
(let ((buffer-read-only nil)
(n (length *flashcard-words*)))
(if (= n 0)
(progn
(erase-buffer)
(insert "\nNo more words left!\n\nPress any key to quit.")
(read-char-exclusive)
(kill-buffer nil)
(message "Bye!")
nil)
(goto-char (point-min))
(search-forward-regexp "There are [0-9]+ words in the database.")
(replace-match (format "There are %d words in the database." n))
(goto-char (point-min))
(search-forward-regexp "Last removed word: .*$")
(replace-match (concat "Last removed word: "))
(insert str)
t)))
(defun flashcard-message (str)
(let ((buffer-read-only nil))
(goto-char (point-max))
(search-backward-regexp "^=> .*$")
(replace-match "=> ")
(insert str)))
(defun flashcard-show-answer ()
(interactive)
(when (eq *flashcard-state* 'question)
(let ((word (aref *flashcard-words* (1- *flashcard-next*))))
(flashcard-message (concat (cadr word) " = " (car word))))
(setq *flashcard-state* 'answer)))
(defun flashcard-show-next ()
(interactive)
(when (eq *flashcard-state* 'answer)
(flashcard-next)
(flashcard-message (cadr (aref *flashcard-words* (1- *flashcard-next*))))
(setq *flashcard-state* 'question)))
(defun flashcard-remove-and-show-next ()
(interactive)
(when (eq *flashcard-state* 'answer)
(let ((word (aref *flashcard-words* (1- *flashcard-next*))))
(setq *flashcard-words* (remove word *flashcard-words*)
*flashcard-next* (1- *flashcard-next*))
(when (flashcard-show-removal (format "%s (%s)" (cadr word) (car word)))
(flashcard-show-next)))))
(defun flashcard-quit ()
(interactive)
(let ((buffer-read-only nil))
(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)))))
(setq cursor-type t)
(message "Bye!")))
(defun flashcard ()
"Display flashcards in a new buffer.
Emacs Flashcards searches the current buffer for lines of the format
`solution = definition' and tests the user if he remembers them."
(interactive)
(dolist (i '(*flashcard-words* *flashcard-next* *flashcard-state*))
(kill-local-variable i))
(flashcard-init)
(switch-to-buffer (generate-new-buffer-name "*flashcard*"))
(dolist (i '(*flashcard-words* *flashcard-next* *flashcard-state*))
(make-local-variable i))
(flashcard-help)
(setq cursor-type nil)
(make-local-variable 'show-paren-mode)
(show-paren-mode 0)
(setq buffer-read-only t)
(local-set-key (kbd "SPC") 'flashcard-show-answer)
(local-set-key (kbd "n") 'flashcard-show-next)
(local-set-key (kbd "r") 'flashcard-remove-and-show-next)
(local-set-key (kbd "q") 'flashcard-quit)
(setq *flashcard-state* 'answer)
(flashcard-show-next))