diff options
-rw-r--r-- | gnosis.el | 117 |
1 files changed, 90 insertions, 27 deletions
@@ -125,7 +125,7 @@ When nil, the image will be displayed at its original size." (defconst gnosis-db-version 2 "Gnosis database version.") -(defvar gnosis-note-types '("MCQ" "Cloze" "Basic" "Double" "y-or-n") +(defvar gnosis-note-types '("MCQ" "MC-Cloze" "Cloze" "Basic" "Double" "y-or-n") "Gnosis available note types.") (defvar gnosis-previous-note-tags '() @@ -150,6 +150,14 @@ car value is the prompt, cdr is the prewritten string.") car value is the prompt, cdr is the prewritten string.") +(defvar gnosis-mc-cloze-guidance + '("MC-Cloze format example: This is an example correct-option&-option2&-option3" . "Example correct-option&-option2&-option3")) + +(defcustom gnosis-mc-cloze-separator "&-" + "Sseparator for choices on multiple choice clozes." + :type 'string + :group 'gnosis) + (defcustom gnosis-mcq-separator "\n--\n" "Separator for stem field and options in mcq note type. @@ -361,7 +369,7 @@ SUCCESS is t when user-input is correct, else nil" (let ((hint (or hint ""))) (goto-char (point-max)) (insert - (propertize "\n\n-----\n" 'face 'gnosis-face-separator) + (propertize "\n-----\n" 'face 'gnosis-face-separator) (propertize hint 'face 'gnosis-face-hint)))) (cl-defun gnosis-display-cloze-reveal (&key (cloze-char gnosis-cloze-string) replace (success t) (face nil)) @@ -496,7 +504,7 @@ Set SPLIT to t to split all input given." (error "No decks found. Please create a deck first with `gnosis-add-deck'")) (if id (gnosis-get 'name 'decks `(= id ,id)) - (gnosis-completing-read "Deck: " (gnosis-select 'name 'decks)))) + (funcall gnosis-completing-read-function "Deck: " (gnosis-select 'name 'decks)))) (cl-defun gnosis--get-deck-id (&optional (deck (gnosis--get-deck-name))) "Return id for DECK name." @@ -842,6 +850,34 @@ See `gnosis-add-note--cloze' for more reference." :images (gnosis-select-images) :tags (gnosis-prompt-tags--split gnosis-previous-note-tags))) +(cl-defun gnosis-mc-cloze-extract-options (str &optional (char gnosis-mc-cloze-separator)) + "Extract options for MC-CLOZE note type from STR. + +CHAR: separator for mc-cloze, default to `gnosis-mc-cloze-separator'" + (cl-remove-if + #'null + (mapcar (lambda (s) + (when (string-match-p (regexp-quote char) s) + (split-string s (regexp-quote char)))) + (split-string str " ")))) + +(defun gnosis-add-note-mc-cloze (deck) + "Add MC-CLOZE note type to DECK. + +MC-CLOZE (Multiple Choice Cloze) note type consists of a sentence with a +single cloze, for which user will be prompted to select the correct +answer." + (interactive) + (let* ((input (gnosis-read-string-from-buffer (or (car gnosis-mc-cloze-guidance) "") + (or (cdr gnosis-mc-cloze-guidance) ""))) + (question (gnosis-mc-cloze-remove-separator input)) + (options (gnosis-mc-cloze-extract-options input)) + (tags (gnosis-prompt-tags--split gnosis-previous-note-tags)) + (images (gnosis-select-images))) + (cl-loop for option in options + do (gnosis-add-note-fields deck "mc-cloze" question option (car option) + "extra" tags 0 (car images) (cdr images))))) + ;;;###autoload (defun gnosis-add-note (&optional deck type) "Create note(s) as TYPE interactively. @@ -853,7 +889,7 @@ TYPE: Type of gnosis note, must be one of `gnosis-note-types'" (unless (y-or-n-p "You are using a testing environment! Continue?") (error "Aborted"))) (let* ((deck (or deck (gnosis--get-deck-name))) - (type (or type (funcall gnosis-completing-read-function "Type: " gnosis-note-types nil t))) + (type (or type (completing-read "Type: " gnosis-note-types nil t))) (func-name (intern (format "gnosis-add-note-%s" (downcase type))))) (if (fboundp func-name) (progn (funcall func-name deck) @@ -915,6 +951,12 @@ Valid cloze formats include: (mapcar (lambda (tag-group) (nreverse (cdr tag-group))) (nreverse result-alist)))) +(defun gnosis-mc-cloze-remove-separator (string &optional separator) + "Remove SEPARATOR and all followed words from STRING." + (let* ((separator (or separator gnosis-mc-cloze-separator)) + (result (replace-regexp-in-string (format "%s[^ ]*" separator) "" string))) + result)) + (defun gnosis-compare-strings (str1 str2) "Compare STR1 and STR2. @@ -1269,6 +1311,26 @@ Used to reveal all clozes left with `gnosis-face-cloze-unanswered' face." (gnosis-display-next-review id success) success)) +(defun gnosis-review-mc-cloze (id) + "Review MC-CLOZE note of ID." + (let ((main (gnosis-get 'main 'notes `(= id ,id))) + ;; Cloze needs to be a list, we take car as the answer + (cloze (list (gnosis-get 'answer 'notes `(= id ,id)))) + (user-choice nil) + (success nil)) + (gnosis-display-cloze-sentence main cloze) + (gnosis-display-image id) + (setf user-choice (gnosis-mcq-answer id) + success (string= user-choice (car cloze))) + (gnosis-display-cloze-reveal :replace (car cloze) + :success success) + ;; Display user answer only upon failure + (unless success + (gnosis-display-cloze-user-answer user-choice)) + (gnosis-display-extra id) + (gnosis-display-next-review id success) + success)) + (defun gnosis-review-note (id) "Start review for note with value of id ID, if note is unsuspended." (when (gnosis-suspended-p id) @@ -1319,8 +1381,7 @@ NOTE-NUM: The number of notes reviewed in the session." (shell-command (format "%s %s %s" git "add" (shell-quote-argument "gnosis.db"))) (shell-command (format "%s %s %s" git "commit -m" (shell-quote-argument (format "Total notes for session: %d" note-num))))) - (when (and gnosis-vc-auto-push - (not gnosis-testing)) + (when (and gnosis-vc-auto-push (not gnosis-testing)) (gnosis-vc-push)) (message "Review session finished. %d notes reviewed." note-num))) @@ -1583,17 +1644,16 @@ SUSPEND: Suspend note, 0 for unsuspend, 1 for suspend" (cl-assert (or (stringp options) (listp options)) nil "Options must be a string, or a list for MCQ") (cl-assert (or (= suspend 0) (= suspend 1)) nil "Suspend must be either 0 or 1") ;; Construct the update clause for the emacsql update statement. - (cl-loop for (field . value) in - `((main . ,main) - (options . ,options) - (answer . ,answer) - (tags . ,tags) - (extra-notes . ,extra-notes) - (images . ,image) - (extra-image . ,second-image) - (ef . ',ef) - (ff . ,ff) - (suspend . ,suspend)) + (cl-loop for (field . value) in `((main . ,main) + (options . ,options) + (answer . ,answer) + (tags . ,tags) + (extra-notes . ,extra-notes) + (images . ,image) + (extra-image . ,second-image) + (ef . ',ef) + (ff . ,ff) + (suspend . ,suspend)) when value do (cond ((memq field '(extra-notes images extra-image)) (gnosis-update 'extras `(= ,field ,value) `(= id ,id))) @@ -1678,10 +1738,10 @@ to improve readability." (interactive) ;; Refresh modeline (setq gnosis-due-notes-total (length (gnosis-review-get-due-notes))) - (let ((review-type (funcall gnosis-completing-read-function "Review: " '("Due notes" - "Due notes of deck" - "Due notes of specified tag(s)" - "All notes of tag(s)")))) + (let ((review-type (gnosis-completing-read "Review: " '("Due notes" + "Due notes of deck" + "Due notes of specified tag(s)" + "All notes of tag(s)")))) (pcase review-type ("Due notes" (gnosis-review-session (gnosis-collect-note-ids :due t))) ("Due notes of deck" (gnosis-review-session (gnosis-collect-note-ids :due t :deck (gnosis--get-deck-id)))) @@ -1767,6 +1827,8 @@ Return note ids for notes that match QUERY." else collect (replace-regexp-in-string "\n" " " (format "%s" item)))) +;; TODO: Rewrite. Tags should be an input of strings, interactive +;; handling should be done by "helper" funcs (cl-defun gnosis-collect-note-ids (&key (tags nil) (due nil) (deck nil) (query nil)) "Return list of note ids based on TAGS, DUE, DECKS, QUERY. @@ -1919,12 +1981,13 @@ for dashboard type. DASHBOARD-TYPE: either 'Notes' or 'Decks' to display the respective dashboard." (interactive) - (let ((dashboard-type (or dashboard-type (cadr (read-multiple-choice - "Display dashboard for:" - '((?n "notes") - (?d "decks") - (?t "tags") - (?s "search"))))))) + (let ((dashboard-type (or dashboard-type + (cadr (read-multiple-choice + "Display dashboard for:" + '((?n "notes") + (?d "decks") + (?t "tags") + (?s "search"))))))) (if note-ids (gnosis-dashboard-output-notes note-ids) (pcase dashboard-type ("notes" (gnosis-dashboard-output-notes (gnosis-collect-note-ids))) |