summaryrefslogtreecommitdiff
path: root/org-fc-type-text-input.el
blob: 1327633d1d9eaa6cec26df7aee062dd116703190 (about) (plain)
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
;;; org-fc-type-text-input.el --- Text-input card type -*- lexical-binding: t; -*-

;; Copyright (C) 2020-2021  Leon Rische

;; Author: Leon Rische <emacs@leonrische.me>
;; Url: https://www.leonrische.me/pages/org_flashcards.html
;; Package-requires: ((emacs "26.3") (org "9.3"))
;; Version: 0.1.0

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;;
;;; Code:

(require 'org-fc-diff)

(defun org-fc-text-input-content ()
  "Return the first line of a cards (back) contents.
Returns a pair (pos . string).  If the card has a 'Back' heading,
its content is used, otherwise, the main content is used.  This
function is expected to be called with point on a heading."
  (save-excursion
    ;; Go to main or "Back" heading
    (if-let ((pos (org-fc-back-heading-position)))
        (goto-char pos))

    ;; Skip metadata & drawers
    (forward-line)
    (while (or (looking-at-p org-scheduled-regexp)
               (looking-at-p org-deadline-regexp))
      (forward-line))
    (while (looking-at org-drawer-regexp)
      (if (re-search-forward ":END:" nil t)
          (forward-line)
        (error "No :END: found for drawer")))
    (unless (looking-at-p org-heading-regexp)
      (cons
       (point)
       (buffer-substring-no-properties (point) (point-at-eol))))))

(defun org-fc-type-text-input-init ()
  "Mark headline as card of the text-input type."
  (interactive)
  (unless (org-fc-text-input-content)
    (error "Card contains content"))
  (org-fc--init-card "text-input")
  (org-fc-review-data-update '("front")))

(defun org-fc-type-text-input-setup (_position)
  "Prepare a text-input card for review."
  (interactive)
  ;; Hide answer
  (outline-hide-subtree)
  (when (org-fc-has-back-heading-p)
    (org-show-entry)
    (org-fc-with-point-at-back-heading (org-show-set-visibility 'minimal)))
  ;; Prompt user, create diff overlay
  (let* ((pos-content (org-fc-text-input-content))
         (content (cdr pos-content))
         (start (car pos-content))
         (end (+ start (length content)))
         (deemph (org-fc-deemphasize content))
         (diff (org-fc-diff (read-string "Answer: ") (cdr deemph))))
    ;; Overlay for user input
    (when (car deemph)
      (setq start (1+ start))
      (setq end (1- end)))
    (org-fc-hide-region start end (car diff))
    ;; Overlay for expected answer, using the newline after the answer
    (if (cdr diff)
        (org-fc-hide-region
         end (1+ end)
         (concat
          " (expected: "
          (if (null (car deemph))
              (cdr diff)
            (org-fc-emphasize
             (concat (car deemph) (cdr diff) (car deemph))))
          ")\n"))))
  ;; Reveal answer & diff
  (save-excursion
    (org-show-entry)
    (org-show-children)
    (org-fc-with-point-at-back-heading
     (org-show-entry)
     (org-show-children)
     (org-fc-show-latex))))

(org-fc-register-type
 'text-input
 'org-fc-type-text-input-setup
 nil
 'org-fc-noop)

;;; Footer

(provide 'org-fc-type-text-input)

;;; org-fc-type-text-input.el ends here