anki-capture

Like org-capture for Anki notes
git clone git://git.wrycode.com/wrycode/anki-capture.git
Log | Files | Refs | README

anki-capture.el (7200B)


      1 ;;; anki-capture.el --- Quickly capture notes directly into Anki. -*- lexical-binding: t; -*-
      2 ;;
      3 ;; © 2020 Nick Econopouly, Cheong Yiufung
      4 ;;
      5 ;; URL: http://git.wrycode.com/wrycode/anki-capture/log.html
      6 ;;
      7 ;;; Commentary:
      8 ;; Perhaps you want to use Emacs and Org mode for quickly adding notes to
      9 ;; Anki, but you do not want to deal with storing and organizing the
     10 ;; notes in text format. 'anki-capture' is an 'org-capture'-like
     11 ;; interface for adding notes directly to Anki from anywhere in Emacs.
     12 ;;
     13 ;;
     14 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     15 ;;
     16 ;; This program is free software: you can redistribute it and/or modify
     17 ;; it under the terms of the GNU General Public License as published by
     18 ;; the Free Software Foundation, either version 3 of the License, or (at
     19 ;; your option) any later version.
     20 ;;
     21 ;; This program is distributed in the hope that it will be useful, but
     22 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
     23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     24 ;; General Public License for more details.
     25 ;;
     26 ;; You should have received a copy of the GNU General Public License
     27 ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
     28 ;;
     29 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     30 ;;; Code:
     31 
     32 (require 'anki-editor)
     33 
     34 ;; These are global vars that you can set yourself in your init
     35 (defcustom anki-capture-file nil
     36   "Optional file to save anki-capture notes in.")
     37 (defcustom anki-capture-auto-yank nil
     38   "Whether to automatically yank the clipboard into your notes.")
     39 (defcustom anki-capture-deck nil
     40   "Current Anki deck for anki-capture.")
     41 (defcustom anki-capture-note-type nil
     42   "Current Anki note type for anki-capture.")
     43 (defcustom anki-capture-tags nil
     44   "Current tags for anki-capture.")
     45 
     46 
     47 ;; Thank you Cheong Yiufung for these functions!
     48 (defun anki-editor-cloze-region-auto-incr (&optional arg)
     49   "Cloze region without hint and increase card number."
     50   (interactive)
     51   (anki-editor-cloze-region my-anki-editor-cloze-number "")
     52   (setq my-anki-editor-cloze-number (1+ my-anki-editor-cloze-number))
     53   (forward-sexp))
     54 (defun anki-editor-cloze-region-dont-incr (&optional arg)
     55   "Cloze region without hint using the previous card number."
     56   (interactive)
     57   (anki-editor-cloze-region (1- my-anki-editor-cloze-number) "")
     58   (forward-sexp))
     59 (defun anki-editor-reset-cloze-number (&optional arg)
     60   "Reset cloze number to ARG or 1"
     61   (interactive)
     62   (setq my-anki-editor-cloze-number (or arg 1)))
     63 
     64 (defun anki-capture-insert-note-skeleton ()
     65   "Insert an anki-capture note subtree."
     66   (insert "* Captured note from Emacs")
     67   (org-set-tags anki-capture-tags)
     68   (move-end-of-line nil)
     69   (newline)
     70   (org-set-property anki-editor-prop-deck anki-capture-deck)
     71   (org-set-property anki-editor-prop-note-type anki-capture-note-type)
     72 
     73   (let ((fields (anki-editor--anki-connect-invoke-result
     74 		 "modelFieldNames" `((modelName . ,anki-capture-note-type)))))
     75 
     76     ;; leave point at the first field to start entering content immediately
     77     (newline)
     78     (insert "** ")
     79     (insert (car fields))
     80     (newline)
     81 
     82     ;; rest of the fields
     83     (save-excursion
     84       (dolist (field (cdr fields))
     85 	(newline)
     86 	(insert "** ")
     87 	(insert field)))))
     88 
     89 (defvar anki-capture-mode-map
     90   (let ((map (make-sparse-keymap)))
     91     (define-key map "\C-c\C-c" #'anki-capture-finish)
     92     (define-key map "\C-c\C-k" #'anki-capture-cancel)
     93     map)
     94   "Keymap for `anki-capture-mode', a minor mode.
     95 Use this map to set additional keybindings anki-capture buffers.")
     96 
     97 (defvar anki-capture-mode-hook nil
     98   "Hook for the `anki-capture-mode' minor mode.")
     99 
    100 (define-minor-mode anki-capture-mode
    101   "Minor mode for special key bindings in an anki-capture buffer.
    102 
    103 Turning on this mode runs the normal hook `anki-capture-mode-hook'."
    104   nil " Cap" anki-capture-mode-map
    105   (setq-local
    106    header-line-format
    107    (substitute-command-keys
    108     "\\<anki-capture-mode-map>Anki Capture buffer.  Finish: \
    109 `\\[anki-capture-finish]' Cancel: `\\[anki-capture-cancel]'")))
    110 
    111 (defun anki-capture-finish ()
    112   (interactive)
    113   (anki-editor-mode)			       ;; this is necessary to properly push inline images
    114   (call-interactively 'anki-editor-push-notes) ;; pushes current note
    115   (kill-buffer "*anki-capture*"))
    116 
    117 (defun anki-capture-cancel ()
    118   (interactive)
    119   ;; only deletes the current note because we are in an indirect buffer
    120   (delete-region (point-min) (point-max))
    121   (kill-buffer "*anki-capture*"))
    122 
    123 ;;;###autoload
    124 (defun anki-capture (prefix)
    125   "This is a unified command to handle adding Anki cards from
    126 anywhere in Emacs. 'anki-capture-finish' will close the temporary
    127 buffer and sync the note to Anki. 'anki-capture-file' must be set
    128 if you want to persistently save a copy of your captured notes,
    129 otherwise you can view notes from this session in a buffer called
    130 '*anki-capture-storage-buffer*'.
    131 
    132 The first time you run this command (per Emacs session), you will
    133 be asked to choose three \"note settings\": the deck, note type,
    134 and tags for the card you are adding. Subsequent calls will just
    135 assume you want all of the same settings. To change settings use
    136 prefix arguments as follows:
    137 
    138 Use the single prefix argument (C-u) if you want to change some
    139 of the note settings and keep some of them. Your previous setting
    140 will automatically be filled in, so you can type enter to keep
    141 it, or delete it (C-a C-k) to choose a new option.
    142 
    143 Use the double prefix argument (C-u C-u) if you want to start
    144 fresh and choose new note settings."
    145 
    146   (interactive "P")
    147 
    148   (let ((storage-buffer (if anki-capture-file (find-file-noselect anki-capture-file)
    149 			  (get-buffer-create "*anki-capture-storage-buffer*"))))
    150 
    151     ;; Prompt the user for note options
    152     (if (or prefix (not (and anki-capture-deck anki-capture-note-type anki-capture-tags)))
    153 	;; initial input for completing read
    154 	(let* ((double-prefix? (equal prefix '(16)))
    155 	       (default-type (unless double-prefix? anki-capture-note-type))
    156 	       (default-tags (unless double-prefix? anki-capture-tags))
    157 	       (default-deck (unless double-prefix? anki-capture-deck)))
    158 	  ;; this sets the global note setting variables!
    159 	  (setq anki-capture-note-type
    160 		(completing-read "Choose a note type: " (sort (anki-editor-note-types) #'string-lessp)
    161 				 nil t default-type)
    162 		anki-capture-deck
    163 		(completing-read "Choose a deck: " (sort (anki-editor-deck-names) #'string-lessp)
    164 				 nil nil default-deck)
    165 		anki-capture-tags
    166 		(completing-read-multiple "Choose tags (comma-separated, press TAB to complete): "
    167 					  (anki-editor-all-tags)
    168 					  nil nil (mapconcat 'print default-tags ",")))))
    169 
    170     ;; if the user hasn't set anki-capture-file we have to make sure storage-buffer is in org-mode
    171     (with-current-buffer storage-buffer (org-mode))
    172 
    173     ;; org-capture-like buffer
    174     (switch-to-buffer (make-indirect-buffer storage-buffer "*anki-capture*" t))
    175     (anki-capture-mode)
    176 
    177     ;; get ready to insert a note
    178     (goto-char (point-max))
    179     (newline)
    180     (narrow-to-region (point) (point-max))
    181     (anki-capture-insert-note-skeleton)
    182     (if anki-capture-auto-yank (yank))
    183     (org-show-all)
    184     (anki-editor-reset-cloze-number)))
    185 
    186 (provide 'anki-capture)
    187 
    188 ;;; anki-capture.el ends here