Home
Softono
anki-editor

anki-editor

Open source Emacs Lisp
188
Stars
36
Forks
17
Issues
5
Watchers
1 week
Last Commit

About anki-editor

Emacs minor mode for making Anki cards with Org

Platforms

Web Self-hosted

Languages

Emacs Lisp

Links

[[https://github.com/anki-editor/anki-editor/actions/workflows/tests.yml/badge.svg]]

  • anki-editor

anki-editor is an [[https://www.gnu.org/software/emacs/emacs.html][Emacs]] minor mode for making [[https://apps.ankiweb.net][Anki]] cards with [[https://orgmode.org][Org Mode]].

** Installation

*** External Dependencies

| Emacs 28+ | anki-editor may run on older versions, but it's not supported | | [[https://apps.ankiweb.net][Anki]] | Open source Spaced-Repetition Software | | [[https://git.sr.ht/~foosoft/anki-connect][AnkiConnect]] | Anki add-on to expose an http API for interacting with Anki | | [[https://curl.se][curl]] | Unix CLI tool used for making requests to AnkiConnect's API |

Additionally, anki-editor requires Anki to be running (see [[https://github.com/orgtre/anki-editor/issues/5#issuecomment-1295857747][issue]] for explanation).

*** Using ~package-vc.el~

If you are on Emacs 29 and newer, you can use ~package-vc-install~:

+BEGIN_SRC emacs-lisp

(package-vc-install '(anki-editor . (:url "https://github.com/anki-editor/anki-editor")))

+END_SRC

Additionally, [[https://github.com/slotThe/vc-use-package][vc-use-package]] provides use-package integration:

+begin_src emacs-lisp

(use-package anki-editor :vc (:fetcher github :repo anki-editor/anki-editor))

+end_src

Alternatively, if you're on Emacs 30, a ~:vc~ keyword is built into use-package:

+begin_src emacs-lisp

(use-package anki-editor :vc (:url "https://github.com/anki-editor/anki-editor" :rev :newest))

+end_src

*** Manually

If you're using the built-in =package.el= package manager, first manually download this repository and then type the following in Emacs:

: M-x package-install-file [RET] "path/to/anki-editor.el" [RET]

You can also first visit =anki-editor.el= in an Emacs buffer and then type =M-x package-install-from-buffer [RET]=. Both will install the anki-editor package, including uninstalled Elisp dependencies, into =package-user-dir= (=~/.emacs.d/elpa= by default), generate autoloads, and set up =load-path=.

*** Using straight.el

If you're using [[https://github.com/radian-software/straight.el][straight.el]] as package manager, install by adding this to your =init.el=:

+begin_src elisp

(use-package anki-editor :defer t :straight (:repo "anki-editor/anki-editor"))

+end_src

*** With Doom Emacs

If you use Doom Emacs, add the following to ~packages.el~ and ~config.el~ respectively.

+begin_src emacs-lisp

(package! anki-editor :recipe (:host github :repo "anki-editor/anki-editor"))

+end_src

+begin_src emacs-lisp

(use-package! anki-editor)

+end_src

*** With Spacemacs

If you use Spacemacs, you can add ~anki-editor~ to ~dotspacemacs-additional-packages~:

+begin_src emacs-lisp

(setq-default ;; … dotspacemacs-additional-packages '((anki-editor :location (recipe :fetcher github :repo "anki-editor/anki-editor"))) ;; … )

+end_src

** Usage

* The Layout of Notes ** Note and Fields General Information The power of this mode comes from the builtin HTML export backend provided by Org, which enables you to use almost all the Org constructs for writing Anki notes: lists, code blocks, tables, latex and so on.

The structure of a note is as follows, which is inspired by ~org-drill~. Check out [[./examples.org][examples.org]] for more examples.

+BEGIN_SRC org

 ,* Raining                                                      :vocab:idioms:
   :PROPERTIES:
   :ANKI_DECK: English
   :ANKI_NOTE_TYPE: Basic (and reversed card)
   :ANKI_TAGS: vocab idioms
   :END:
 ,** Front
    (it's) raining cats and dogs
 ,** Back
    it's raining very hard
 ,* Is there a shorter way to write notes?
    :PROPERTIES:
    :ANKI_NOTE_TYPE: Basic
    :END:

 ,** Back

    Yes, like this one, Front is missing, ~anki-editor~ will use note
    heading as Front.  This is neat as sometimes it's verbose to repeat
    the same content in note heading and first field.

    This works for all note types, just make one field absent and
    ~anki-editor~ will use note heading as that missing field.

 ,* Is there a an even shorter way to write notes?
    :PROPERTIES:
    :ANKI_NOTE_TYPE: Basic
    :END:

    Yes, like this one, Front and Back is missing, ~anki-editor~ will use note
    heading as Front and the text after as Back.  This is neat as sometimes it's verbose to repeat
    the same content in note heading and first field.

    This works for all note types, just make the first 2 fields absent and
    ~anki-editor~ will use note heading as first field and the text below the heading as second field.

 ,* You can extract a field value from an org-property
    :PROPERTIES:
    :ANKI_NOTE_TYPE: Basic
    :ANKI_FIELD_FRONT: Can one define an anki-field inside an org-mode property?
    :ANKI_PREPEND_HEADING: nil
    :END:

    Yes. In this example, =anki-editor=  will use the =ANKI_FIELD_FRONT= property value as
    a front side of the Anki card and the body of the card as its back.

 ,** Front
    Notice that property fields will override subheading fields.
    This block will be skipped

 ,* Notes can be nested and intermixed with regular content
    :PROPERTIES:
    :ANKI_NOTE_TYPE: Basic
    :ANKI_FIELD_FRONT: Can you have a note embedded in a higher level heading?
    :ANKI_NO_SUBHEADING_FIELDS: t
    :END:

    Yes, =anki-editor= can be told to not interpret subheadings as fields at all.

 ,** More on this topic
    This will not be considered as a field of the above note. It can be also turned into a note on its own.

+END_SRC

  • Anki deck is provided by ~ANKI_DECK~ property. This property is retrieved with inheritance, that is to say, it can be put in any ancestor entries or at top of the file by ~#+PROPERTY: ANKI_DECK DeckName~.
  • Sub decks (nested decks) are supported via ~::~ delimiters. For example, to push a card to the sub deck ~SubDeck~ inside of ~SuperDeck~, set ~:ANKI_DECK:~ to ~SuperDeck::SubDeck~.
  • ~ANKI_NOTE_TYPE~ property is to specify the Anki note type of a note and is also required for identifying an Anki note entry.
  • Anki tags can be provided in two ways:
    1. With a ~ANKI_TAGS~ property, multiple tags are separated by spaces
    2. With Org tags [fn:1], this could be turned off if you would like to keep Org tags separated from Anki tags
  • 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.

[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 Anki tags before they can be used in Org without any surprise. **** Controlling HTML Formatting By default, anki-editor converts Org syntax to HTML when exporting to Anki but anki-editor provides a few mechanisms for disabling this at different levels.

The =anki-editor-export-note-fields-on-push= elisp variable can be set to nil in your emacs config file to globally disable exporting to HTML.

The =:ANKI_FORMAT: nil= property can be used at the note level to disable this conversion for the entire note.

If you want to use both raw text fields and HTML-converted fields within a single note, you can now use the =# raw= prefix within a field to indicate that the field's content should be treated as raw text, bypassing HTML conversion. Any spaces, tabs, or newlines immediately following =# raw= are ignored.

+BEGIN_SRC org

,* Example Note with Mixed Formatting :PROPERTIES: :ANKI_NOTE_TYPE: Basic :END:

,** Front This field will be converted to HTML.

  • This is a list.
  • It will be rendered as an HTML list.

,** Back

raw

This field will be treated as RAW text. It will be sent to Anki exactly as written.

+END_SRC

*** Commands To see the docs for the most recent commands use M-x describe-function (for more info see [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Name-Help.html][Emacs Manual - Help Commands]])

| Command | Description | |------------------------------------+--------------------------------------------------------------------------------------------------------------------------------| | anki-editor-mode | Toggle this minor mode. | | anki-editor-push-notes | Push new and changed notes to Anki. Additional arguments can be used to restrict the range of notes. | | anki-editor-push-new-notes | Similar to ~anki-editor-push-notes~ but only push new notes, those that are without ~ANKI_NOTE_ID~. | | anki-editor-retry-failed-notes | Similar to ~anki-editor-push-notes~ except that it only pushes notes with ~ANKI_FAILURE_REASON~. | | anki-editor-force-push-notes | Similar to ~anki-editor-push-notes~ except that it pushes unchanged notes as well. | | anki-editor-insert-note | Insert a note entry like ~M-RET~, interactively. When note heading is not provided or is blank, it's used as the first field. | | 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-convert-region-to-html | Convert and replace region to HTML. | | anki-editor-api-check | Check if correct version of AnkiConnect is running. | | anki-editor-sync-collections | Synchronize your local anki collection. | | 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. |

*** Variables To see the docs for the most recent commands use M-x describe-variable (for more info see [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Name-Help.html][Emacs Manual - Help Commands]])

| Name | Default Value | Description | |-----------------------------------------------+------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | anki-editor-api-host | "127.0.0.1" | The network address AnkiConnect is listening. | | anki-editor-api-port | "8765" | The port number AnkiConnect is listening. | | anki-editor-break-consecutive-braces-in-latex | nil | If non-nil, consecutive "}" will be automatically separated by spaces to prevent early-closing of cloze. | | 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-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-latex-style | builtin | The style of latex to translate into. | | anki-editor-include-default-style | t | Whether or not to include org-html-style-default' when usinganki-editor-copy-styles'. | | anki-editor-html-head | nil | Additional html tags to append to card stylings when using `anki-editor-copy-styles'. | | anki-editor-note-match | nil | Additional matching string for mapping through anki note headings. | | anki-editor-export-note-fields-on-push | t | Whether to export Org syntax to HTML. | | anki-editor-force-update | nil | Whether to push notes even if changes are not detected in the note contents. | | anki-editor-allow-duplicates | nil | If nil, pushing notes will fail if duplicate note is detected. | | anki-editor-default-note-type | "Basic" | Default note type when creating an anki-editor note in org. | | anki-editor-latex-display-math-div | nil | Whether to wrap latex fragments with an extra div when exporting. Either nil, for no extra wrapping, or a string value, which will be used for the class name of the wrapping div when exporting. |

*** Functions and Macros

**** anki-editor-map-note-entries

Simple wrapper that calls ~org-map-entries~ with
~&ANKI_NOTE_TYPE<>\"\"~ appended to MATCH.

**** anki-editor-api-call

Invoke AnkiConnect with ACTION and PARAMS.

**** anki-editor-api-call-result

Calls above, returns result field or raise an error.

**** anki-editor-api-enqueue-request

Queue ACTION with PARAMS for later dispatch.  SUCCESS and ERROR
callbacks are invoked by ~anki-editor-api-dispatch-queue~ when the
response arrives.

**** anki-editor-api-dispatch-queue

Dispatch all enqueued requests as a single AnkiConnect ~multi~
call, then run each request's success/error callback against the
corresponding response.  Returns a property list with ~:count~,
~:successes~, ~:errors~, and ~:results~.

Usage:
#+begin_src elisp
  (let ((collected (make-hash-table)))
    (dolist (action '(deckNames modelNames getTags))
      (anki-editor-api-enqueue-request
       action nil
       :success (lambda (res) (puthash action res collected))))
    (anki-editor-api-enqueue-request
     'findNotes (list :query "deck:Default")
     :success (lambda (res) (puthash 'findNotes res collected)))
    (anki-editor-api-dispatch-queue)
    collected)
#+end_src

**** anki-editor-note-at-point

Make a note struct from current entry.

**** anki-editor-find-notes

Find notes with QUERY.

**** anki-editor-copy-styles

Copy ~org-html-style-default~ and ~anki-editor-html-head~ to Anki card stylings.

**** anki-editor-remove-styles

Remove from card stylings html tags generated by this mode.

** Limitations

*** Tags between Anki and Org

Because the set of characters allowed in tags is different between Anki and Org, you have to make sure that tags from Anki are compatible with Org and tags in Org could be recognized by Anki.

*** Working with Anki add-ons

This package might not work well with certain Anki add-ons especially those who extend the builtin Anki note editor to automatically fill note field content (e.g. ~Add note id~).

*** One Way Sync with Anki

To sync anki notes and decks to org see ([[https://github.com/orgtre/ankiorg][orgtre/ankiorg]]).

The following items are not synchronized to org:

  • Deletion of Notes
  • Deck Changes

** Demo

[[./demo.gif]]

** History

This repository originated as a fork of [[https://github.com/louietan/anki-editor][louietan/anki-editor]] and has since become the de-facto version. For more information check out an earlier readme [[https://github.com/anki-editor/anki-editor/blob/f5f22417b4572e7fc247a4e82c02817b4de0138e/README.org#anki-editor][here]].