TIL: 用 Org-mode 列表管理选择题题库
Randy Ridenour 在 博客 上介绍了一种用 Org-mode 有序列表管理选择题的方法:数字编号是题目,字母编号是选项,正确答案后面加 * 。
1. 下面哪个是 Emacs 的默认编辑器? a) Vim b) nano c) Emacs* d) ed 2. Elisp 中哪个函数用于向前搜索正则表达式? a) string-match b) re-search-forward* c) looking-at d) search-forward 3. 下面哪个是 Vim 的默认编辑器? a) Vim* b) nano c) Emacs d) ed
这种格式的好处是 Org-mode 自带列表操作: M-up / M-down 移动题目(连子项一起拖), org-list-repair 重编号。
下面函数可以从题库选题到*scratch*,首先找到题目边界:
(defun my/copy-mcq-to-scratch () "Copy the multiple choice question at point to the *scratch* buffer." (interactive) (save-excursion (let* ((question-start (progn (end-of-line) (if (re-search-backward "^[0-9]+\\." nil t) (point) (error "No question found at point")))) (question-end (progn (goto-char question-start) (forward-line 1) (if (re-search-forward "^[0-9]+\\." nil t) (match-beginning 0) (point-max)))) (text (buffer-substring-no-properties question-start question-end))) (with-current-buffer (get-buffer-create "*scratch*") (goto-char (point-max)) (insert text)))))
向上搜 ^[0-9]+\\. 定位题目开头,向下搜下一个同模式匹配作为题目结尾,中间就是一道完整题目。
删除题目也用同样的边界定位,删完后调 org-list-repair 重编号:
(defun my/delete-mcq-at-point () "Delete the multiple choice question at point." (interactive) (save-excursion (beginning-of-line) (unless (looking-at "[[:space:]]*[0-9]+\\.") (re-search-backward "^[[:space:]]*[0-9]+\\." nil t)) (let ((start (line-beginning-position)) (end (progn (forward-line 1) (if (re-search-forward "^[0-9]+\\." nil t) (match-beginning 0) (point-max))))) (kill-region start end))) (org-list-repair))
这里用 kill-region 而不是 delete-region ,是因为这样删掉的题进 kill ring,还能捞回来。