Doc stuff

This commit is contained in:
louie 2019-09-23 17:13:10 +08:00 committed by louie
parent b6065cd2f7
commit b8f6986ab9
2 changed files with 84 additions and 94 deletions

View file

@ -1,40 +1,40 @@
[[http://melpa.org/#/anki-editor][file:http://melpa.org/packages/anki-editor-badge.svg]] [[http://melpa.org/#/anki-editor][file:http://melpa.org/packages/anki-editor-badge.svg]]
anki-editor -- Emacs minor mode for making Anki cards with Org anki-editor -- An Emacs minor mode for making Anki cards with Org
/Since I'm not a native English speaker, feel free to correct me if /Since I'm not a native English speaker, feel free to correct me if
there were any ambiguity or grammatical mistakes ;-)/ there are any ambiguity or grammatical mistakes ;-)/
* Installation * Installation
*Requirements* *Requirements*
- [[https://github.com/FooSoft/anki-connect#installation][anki-connect]], - [[https://github.com/FooSoft/anki-connect#installation][AnkiConnect]],
an Anki add-on required by this package to interact with Anki. an Anki add-on required by this package to interact with Anki
- curl - curl
If you have [[http://melpa.org/][MELPA]] in your =package-archives=, If you have [[http://melpa.org/][MELPA]] in your ~package-archives~,
just =M-x package-install RET anki-editor RET=, or install it just ~M-x package-install RET anki-editor RET~, or install it
manually by downloading and visiting [[./anki-editor.el][anki-editor.el]] in your manually by downloading and visiting [[./anki-editor.el][anki-editor.el]] in your
emacs buffer, and =M-x package-install-from-buffer RET=. emacs buffer, and ~M-x package-install-from-buffer RET~.
* Usage * Usage
** The Layout of Notes ** The Layout of Notes
Now you can compose Anki notes in Org syntax, e.g. lists, code The power of this mode comes from the builtin HTML export backend
examples, tables, embedded latex, when being submitted to Anki, provided by Org, which enables you to use almost all the Org
they will be converted to HTML by Org-mode's HTML backend with constructs for writing Anki notes: lists, code blocks, tables,
specific markers (e.g. latex) translated to Anki style. latex and so on.
The structure of a note is as follow, which is inspired by The structure of a note is as follows, which is inspired by
=org-drill=. More examples can be found in [[./examples.org][examples.org]]. ~org-drill~. Check out [[./examples.org][examples.org]] for more examples.
#+BEGIN_SRC org #+BEGIN_SRC org
,* Idiom :vocab:idioms: ,* Raining :vocab:idioms:
:PROPERTIES: :PROPERTIES:
:ANKI_DECK: English :ANKI_DECK: English
:ANKI_NOTE_TYPE: Basic (and reversed card) :ANKI_NOTE_TYPE: Basic (and reversed card)
:ANKI_TAGS: languages european_languages :ANKI_TAGS: vocab idioms
:END: :END:
,** Front ,** Front
(it's) raining cats and dogs (it's) raining cats and dogs
@ -42,35 +42,41 @@ there were any ambiguity or grammatical mistakes ;-)/
it's raining very hard it's raining very hard
#+END_SRC #+END_SRC
- An Anki note is an Org entry with =ANKI_NOTE_TYPE= property - Anki deck is provided by ~ANKI_DECK~ property. This property is
- Anki tags can be set in two ways retrieved with inheritance, that is to say, it can be put in any
1. With "ANKI_TAGS" property, multiple tags are separated with space ancestor entries or at top of the file by ~#+PROPERTY: ANKI_DECK
2. With Org tags [fn:1], this could be turned off if you would like to keep Org tags separated from Anki tags DeckName~.
- Other necessary information (e.g. deck, note type) of a note is - ~ANKI_NOTE_TYPE~ property is to specify the Anki note type of a
put in the property drawer of the entry note and is also required for identifying an Anki note entry.
- As the value of =ANKI_DECK= is retrieved with inheritance, you - Anki tags can be provided in two ways:
don't have to set it per note, instead, you could create a deck 1. With a ~ANKI_TAGS~ property, multiple tags are separated by spaces
entry with this property set and put note entries under it, or 2. With Org tags [fn:1], this could be turned off if you would
set it per file by ~#+PROPERTY: ANKI_DECK YourDeck~ like to keep Org tags separated from Anki tags
- Child entries of a note entry are fields - Child entries of a note entry are fields.
Typing all these information by hand could be inefficient and prone
to errors, so this package provides an interactive command
~anki-editor-insert-note~ to help with this and hooks up
auto-completions for decks, note types and tags etc.
** Commands ** Commands
| Command | Brief Description | | Command | Description |
|------------------------------------+--------------------------------------------------------------------------------------| |------------------------------------+---------------------------------------------------------------------------------------------------|
| anki-editor-mode | Toggle this minor mode. |
| anki-editor-push-notes | Push notes to Anki. Additional arguments can be used to restrict the range of notes. | | anki-editor-push-notes | Push notes to Anki. Additional arguments can be used to restrict the range of notes. |
| anki-editor-retry-failure-notes | Same as above, except that it only pushes notes that have =ANKI_FAILURE_REASON=. | | anki-editor-push-new-notes | Similar to ~anki-editor-push-notes~, but push those that are without ~ANKI_NOTE_ID~. |
| anki-editor-insert-note | Insert a note entry like =M-RET=, interactively. | | anki-editor-retry-failed-notes | Similar to ~anki-editor-push-notes~, except that it only pushes notes with ~ANKI_FAILURE_REASON~. |
| anki-editor-cloze-region | Create a cloze deletion from region. | | anki-editor-insert-note | Insert a note entry like ~M-RET~, interactively. |
| anki-editor-delete-notes | Delete notes or the note at point. |
| anki-editor-cloze-dwim | Cloze current active region or a word the under the cursor. |
| anki-editor-export-subtree-to-html | Export the subtree at point to HTML. | | anki-editor-export-subtree-to-html | Export the subtree at point to HTML. |
| anki-editor-convert-region-to-html | Convert and replace region to HTML. | | anki-editor-convert-region-to-html | Convert and replace region to HTML. |
| anki-editor-anki-connect-check | Check if correct version of AnkiConnect is running. |
** Functions | anki-editor-anki-connect-upgrade | Upgrade AnkiConnect. |
| anki-editor-sync-collections | Synchronize your local anki collection. |
| Name | Description | | anki-editor-gui-browse | Open Anki Browser with a query for current note or deck. |
|------------------------------+------------------------------------------------------------| | anki-editor-gui-add-cards | Open Anki Add Cards dialog with presets from current note entry. |
| anki-editor-map-note-entries | Simple wrapper that calls =org-map-entries=. |
| anki-editor-note-at-point | Construct an alist representing a note from current entry. |
** Variables ** Variables
@ -83,6 +89,7 @@ there were any ambiguity or grammatical mistakes ;-)/
| anki-editor-ignored-org-tags | '("export" "noexport") | A list of Org tags that are ignored when constructing notes form entries. | | anki-editor-ignored-org-tags | '("export" "noexport") | A list of Org tags that are ignored when constructing notes form entries. |
| anki-editor-org-tags-as-anki-tags | t | If nil, tags of entries wont't be counted as Anki tags. | | anki-editor-org-tags-as-anki-tags | t | If nil, tags of entries wont't be counted as Anki tags. |
| anki-editor-protected-tags | '("marked" "leech") | A list of tags that won't be deleted from Anki even though they're absent in Org entries. | | anki-editor-protected-tags | '("marked" "leech") | A list of tags that won't be deleted from Anki even though they're absent in Org entries. |
| anki-editor-use-math-jax | nil | Use Anki's built in MathJax support instead of LaTeX. |
* Limitations * Limitations
@ -94,15 +101,14 @@ there were any ambiguity or grammatical mistakes ;-)/
** Working with Anki add-ons ** Working with Anki add-ons
This package may not work well when you are using certain Anki This package might not work well with certain Anki add-ons
add-ons especially those who extend the builtin Anki note editor to especially those who extend the builtin Anki note editor to
automatically fill note field content (e.g. ~Add note id~). automatically fill note field content (e.g. ~Add note id~).
* Troubleshooting * Troubleshooting
In case of a failed operation and this package doesn't provide much In case of a failed operation and this package doesn't provide much
useful information, especially for note creation, don't be useful information, don't be frustrated, see below for some hints.
frustrated, see below for some hints.
1. Decks don't exist in Anki. This package by default doesn't create 1. Decks don't exist in Anki. This package by default doesn't create
decks for you, when trying out this package with ~examples.org~, decks for you, when trying out this package with ~examples.org~,
@ -139,5 +145,5 @@ there were any ambiguity or grammatical mistakes ;-)/
[fn:1] It should be noted that Org only allows letters, numbers, =_= [fn:1] It should be noted that Org only allows letters, numbers, =_=
and =@= in a tag but Anki allows more, so you may have to edit you and ~@~ in a tag but Anki allows more, so you may have to edit you
Anki tags before they can be used in Org without any surprise. Anki tags before they can be used in Org without any surprise.

View file

@ -12,19 +12,17 @@
;; ;;
;;; Commentary: ;;; Commentary:
;; ;;
;; This package is for people who use Anki as SRS but would like to ;; This package is for users of both Emacs and Anki, who'd like to
;; make cards in Org-mode. ;; make Anki cards in Org mode. With this package, Anki cards can be
;; made from an Org buffer like below: (inspired by org-drill)
;; ;;
;; With this package, you can make cards from something like: ;; * Sample :emacs:lisp:programming:
;; (which is inspired by `org-dirll')
;;
;; * Item :emacs:lisp:programming:
;; :PROPERTIES: ;; :PROPERTIES:
;; :ANKI_DECK: Computing ;; :ANKI_DECK: Computing
;; :ANKI_NOTE_TYPE: Basic ;; :ANKI_NOTE_TYPE: Basic
;; :END: ;; :END:
;; ** Front ;; ** Front
;; How to hello world in elisp ? ;; How to say "hello world" in elisp?
;; ** Back ;; ** Back
;; #+BEGIN_SRC emacs-lisp ;; #+BEGIN_SRC emacs-lisp
;; (message "Hello, world!") ;; (message "Hello, world!")
@ -32,13 +30,13 @@
;; ;;
;; This package extends Org-mode's built-in HTML backend to generate ;; This package extends Org-mode's built-in HTML backend to generate
;; HTML for contents of note fields with specific syntax (e.g. latex) ;; HTML for contents of note fields with specific syntax (e.g. latex)
;; translated to Anki style, then save the note to Anki. ;; translated to Anki style.
;; ;;
;; For this package to work, you have to setup these external dependencies: ;; For this package to work, you have to setup these external dependencies:
;; - curl ;; - curl
;; - AnkiConnect, an Anki addon that runs an HTTP server to expose ;; - AnkiConnect, an Anki addon that runs an RPC server over HTTP to expose
;; Anki functions as RESTful APIs, see ;; Anki functions as APIs,
;; https://github.com/FooSoft/anki-connect#installation ;; see https://github.com/FooSoft/anki-connect#installation
;; for installation instructions ;; for installation instructions
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -93,16 +91,19 @@ See https://apps.ankiweb.net/docs/manual.html#latex-conflicts.")
(defcustom anki-editor-org-tags-as-anki-tags (defcustom anki-editor-org-tags-as-anki-tags
t t
"If nil, tags of entries wont't be counted as Anki tags.") "If nil, tags of entries won't be counted as Anki tags.")
(defcustom anki-editor-protected-tags (defcustom anki-editor-protected-tags
'("marked" "leech") '("marked" "leech")
"A list of tags that won't be deleted from Anki even though they're absent in Org entries, such as special tags `marked', `leech'." "A list of tags that won't be deleted from Anki even though
they're absent in Org entries, such as special tags `marked',
`leech'."
:type '(repeat string)) :type '(repeat string))
(defcustom anki-editor-ignored-org-tags (defcustom anki-editor-ignored-org-tags
(append org-export-select-tags org-export-exclude-tags) (append org-export-select-tags org-export-exclude-tags)
"A list of Org tags that are ignored when constructing notes form entries." "A list of Org tags that are ignored when constructing notes
form entries."
:type '(repeat string)) :type '(repeat string))
(defcustom anki-editor-anki-connect-listening-address (defcustom anki-editor-anki-connect-listening-address
@ -142,8 +143,7 @@ See https://apps.ankiweb.net/docs/manual.html#latex-conflicts.")
(request-backend 'curl) (request-backend 'curl)
(json-array-type 'list) (json-array-type 'list)
reply err) reply err)
(request (format "http://%s:%s"
(let ((response (request (format "http://%s:%s"
anki-editor-anki-connect-listening-address anki-editor-anki-connect-listening-address
anki-editor-anki-connect-listening-port) anki-editor-anki-connect-listening-port)
:type "POST" :type "POST"
@ -153,20 +153,13 @@ See https://apps.ankiweb.net/docs/manual.html#latex-conflicts.")
(setq reply data))) (setq reply data)))
:error (cl-function (lambda (&key _ &key error-thrown &allow-other-keys) :error (cl-function (lambda (&key _ &key error-thrown &allow-other-keys)
(setq err (string-trim (cdr error-thrown))))) (setq err (string-trim (cdr error-thrown)))))
:sync t))) :sync t)
;; HACK: With sync set to t, `request' waits for curl process to
;; exit, then response data becomes available, but callbacks
;; might not be called right away but at a later time, that's
;; why here we manually invoke callbacks to receive the result.
(unless (request-response-done-p response)
(request--curl-callback (get-buffer-process (request-response--buffer response)) "finished\n")))
(when err (error "Error communicating with AnkiConnect using cURL: %s" err)) (when err (error "Error communicating with AnkiConnect using cURL: %s" err))
(or reply (error "Got empty reply from AnkiConnect")))) (or reply (error "Got empty reply from AnkiConnect"))))
(defmacro anki-editor--anki-connect-invoke-result (&rest args) (defmacro anki-editor--anki-connect-invoke-result (&rest args)
"Invoke AnkiConnect with ARGS, return the result from response or raise an error." "Invoke AnkiConnect with ARGS, return the result from response
or raise an error."
`(let-alist (anki-editor--anki-connect-invoke ,@args) `(let-alist (anki-editor--anki-connect-invoke ,@args)
(when .error (error .error)) (when .error (error .error))
.result)) .result))
@ -380,7 +373,8 @@ The implementation is borrowed and simplified from ox-html."
;;; Core Functions ;;; Core Functions
(defun anki-editor-map-note-entries (func &optional match scope &rest skip) (defun anki-editor-map-note-entries (func &optional match scope &rest skip)
"Simple wrapper that calls `org-map-entries' with `&ANKI_NOTE_TYPE<>\"\"' appended to MATCH." "Simple wrapper that calls `org-map-entries' with
`&ANKI_NOTE_TYPE<>\"\"' appended to MATCH."
;; disable property inheritance temporarily, or all subheadings of a ;; disable property inheritance temporarily, or all subheadings of a
;; note heading will be counted as note headings as well ;; note heading will be counted as note headings as well
(let ((org-use-property-inheritance nil)) (let ((org-use-property-inheritance nil))
@ -391,7 +385,6 @@ The implementation is borrowed and simplified from ox-html."
Where the subtree is created depends on PREFIX." Where the subtree is created depends on PREFIX."
(org-insert-heading prefix) (org-insert-heading prefix)
(insert heading) (insert heading)
(unless (save-excursion (unless (save-excursion
(org-up-heading-safe) (org-up-heading-safe)
;; don't insert `ANKI_DECK' if some ancestor already has ;; don't insert `ANKI_DECK' if some ancestor already has
@ -399,9 +392,7 @@ Where the subtree is created depends on PREFIX."
(and (not (string-blank-p deck)) (and (not (string-blank-p deck))
(string= deck (org-entry-get-with-inheritance anki-editor-prop-deck)))) (string= deck (org-entry-get-with-inheritance anki-editor-prop-deck))))
(org-set-property anki-editor-prop-deck deck)) (org-set-property anki-editor-prop-deck deck))
(org-set-property anki-editor-prop-note-type note-type) (org-set-property anki-editor-prop-note-type note-type)
(dolist (field fields) (dolist (field fields)
(save-excursion (save-excursion
(org-insert-heading-respect-content) (org-insert-heading-respect-content)
@ -426,12 +417,10 @@ Where the subtree is created depends on PREFIX."
(funcall queue (funcall queue
'createDeck 'createDeck
`((deck . ,(alist-get 'deck note))))) `((deck . ,(alist-get 'deck note)))))
(funcall queue (funcall queue
'addNote 'addNote
`((note . ,(anki-editor--anki-connect-map-note note))) `((note . ,(anki-editor--anki-connect-map-note note)))
#'anki-editor--set-note-id) #'anki-editor--set-note-id)
(funcall queue))) (funcall queue)))
(defun anki-editor--update-note (note) (defun anki-editor--update-note (note)
@ -440,7 +429,6 @@ Where the subtree is created depends on PREFIX."
(funcall queue (funcall queue
'updateNoteFields 'updateNoteFields
`((note . ,(anki-editor--anki-connect-map-note note)))) `((note . ,(anki-editor--anki-connect-map-note note))))
(funcall queue (funcall queue
'notesInfo 'notesInfo
`((notes . (,(alist-get 'note-id note)))) `((notes . (,(alist-get 'note-id note))))
@ -454,19 +442,15 @@ Where the subtree is created depends on PREFIX."
(alist-get 'tags note)) (alist-get 'tags note))
anki-editor-protected-tags)) anki-editor-protected-tags))
(tag-queue (anki-editor--anki-connect-invoke-queue))) (tag-queue (anki-editor--anki-connect-invoke-queue)))
(when tags-to-add (when tags-to-add
(funcall tag-queue (funcall tag-queue
'addTags `((notes . (,(alist-get 'note-id note))) 'addTags `((notes . (,(alist-get 'note-id note)))
(tags . ,(mapconcat #'identity tags-to-add " "))))) (tags . ,(mapconcat #'identity tags-to-add " ")))))
(when tags-to-remove (when tags-to-remove
(funcall tag-queue (funcall tag-queue
'removeTags `((notes . (,(alist-get 'note-id note))) 'removeTags `((notes . (,(alist-get 'note-id note)))
(tags . ,(mapconcat #'identity tags-to-remove " "))))) (tags . ,(mapconcat #'identity tags-to-remove " ")))))
(funcall tag-queue)))) (funcall tag-queue))))
(funcall queue))) (funcall queue)))
(defun anki-editor--set-failure-reason (reason) (defun anki-editor--set-failure-reason (reason)
@ -638,7 +622,7 @@ name and the cdr of which is field content."
;;; Commands ;;; Commands
(defun anki-editor-push-notes (&optional scope match) (defun anki-editor-push-notes (&optional scope match)
"Build notes from headings that can be matched by MATCH within SCOPE and push them to Anki. "Build notes from headings that match MATCH within SCOPE and push them to Anki.
The default search condition `&ANKI_NOTE_TYPE<>\"\"' will always The default search condition `&ANKI_NOTE_TYPE<>\"\"' will always
be appended to MATCH. be appended to MATCH.