~l3kn/org-fc

684cf7435f446fd8b90fe0bffae74726b3a75f7d — Leon Rische 4 months ago 7825855
Add documentation files
A docs/alternative_applications.org => docs/alternative_applications.org +16 -0
@@ 0,0 1,16 @@
#+TITLE: Alternative Applications
#+DATE: [2020-07-17 Fri 00:25]
#+KEYWORDS: fc

In the most abstract sense, this package deals with

1. Attaching timestamped review information to headlines
2. Querying all headings where reviews are due
3. Reviewing due *positions* of headings

While the primary application is learning information using spaced
repetition, at the end, the API should be flexible enough to implement
other kinds of repeating tasks where it is necessary to store data in
addition to the next date.

Some ideas are collected in the [[file:incubator.org][Incubator]].

A docs/cache.org => docs/cache.org +84 -0
@@ 0,0 1,84 @@
#+TITLE: Cache
#+DATE: [2020-07-29 Wed 11:58]
#+KEYWORDS: fc

* Motivation
Even with the AWK based indexer, indexing cards before each review
gets slow if there are a lot of files / cards.

To work around this, the indexer can be run only one time,
caching the results in a hash table.

Advises are added to `delete-file' and `rename-file'
remove / update keys from the hash table.

Changes to existing files & new files are detected with a
~before-save-hook~ on org-mode files.

During a review many files are changed and saved.  To keep this as
fast as possible, instead of re-processing files after each save,
changed files are collected in `org-fc-cache-queue' and reprocessed in
bulk the next time the cache is accessed.

Assuming only a small subset of the flashcard files is changed between
reviews, this is much faster than building the full index ch time.

* Activation
Caching can be activated/deactivated with ~M-x org-fc-cache-mode~.

To activate this mode when Emacs starts,
activate it in your configuration:

#+begin_src emacs-lisp
(org-fc-cache-mode)
#+end_src
* Performance
** Setup
#+begin_src fish :exports results
echo (org-files | xargs grep ":fc:" | wc -l) " cards"
echo (org-files | wc -l) " files"
org-files | xargs wc -l | tail -n 1 | sed "s/total/lines/g"
#+end_src

#+RESULTS:
|  18348 | cards |
|   2478 | files |
| 475860 | lines |
** Benchmarks                                                     :noexport:
#+begin_src emacs-lisp
  (defun my-org-fc-cache-benchmarks ()
    (list
     (list "Dashboard" (benchmark 1 '(org-fc-dashboard 'all)))
     (list "Index Cards in Subdirectory" (benchmark 1 '(length (org-fc-index '(:paths "~/org/deft/")))))
     (list "Index Cards with Tag" (benchmark 1 '(length (org-fc-index '(:filter (tag "spanish"))))))))
#+end_src

#+RESULTS:
: my-org-fc-cache-benchmarks

** AWK
#+begin_src emacs-lisp :exports results
(let ((org-fc-index-function #'org-fc-awk-filter-index))
  (my-org-fc-cache-benchmarks))
#+end_src

#+RESULTS:
| Dashboard                   | Elapsed time: 3.642393s                      |
| Index Cards in Subdirectory | Elapsed time: 0.502262s                      |
| Index Cards with Tag        | Elapsed time: 3.266725s (0.244461s in 1 GCs) |
** Cache
#+begin_src emacs-lisp :exports results
  (let ((org-fc-index-function #'org-fc-cache-filter-index))
    (cons
      (list "Initial Cache Build" (benchmark 1 '(org-fc-cache-build)))
      (my-org-fc-cache-benchmarks)))
#+end_src

#+RESULTS:
| Initial Cache Build         | Elapsed time: 2.982310s |
| Dashboard                   | Elapsed time: 0.673869s |
| Index Cards in Subdirectory | Elapsed time: 0.026792s |
| Index Cards with Tag        | Elapsed time: 0.040647s |

Dashboard performance will be improved once a card's review history is
cached, too.

A docs/card_types.org => docs/card_types.org +115 -0
@@ 0,0 1,115 @@
#+TITLE: Card Types
#+DATE: [2020-08-01 Sat 10:49]
#+KEYWORDS: fc

* Normal Cards
During review, the heading is shown with its "Back" subheading
collapsed, when flipping the card, the back heading is shown,
then the user is asked to rate the review performance.

Positions: =front=
* Text-Input Cards
If the card has a "Back" heading, the first line of its contents is
considered as the expected answer.

If the card is compact (has no back heading), the first line of its
main content is used instead.

This allows adding an e.g. explanation to the card.

Answers can be emphasized (e.g. ~foo~).  In that case, only the text
between the emphasis markers is compared to the user input.

On reviewing the card, the users are prompted to enter their answer,
which is then compared to the expected answer.

The expected answer is overlayed with "<got> (expected: <expected>)",
coloring correct parts in green and incorrect parts in red.

If the provided answer is shorter than the expected one, a sequence of
=-= (colored in red) is prepended / appended to it.

This filler character can be customized via ~org-fc-diff-missing-char~.

Positions: =front=
* Double Cards
Similar to normal cards, but reviewed both in the "Front -> Back"
direction and in the "Back -> Front" direction.

Positions: =front=, =back=
* Cloze Cards
The cards text contains one or more *holes*.  During review, one hole
is hidden while the text of (some) remaining ones is shown.

Flipping the card reveals the text of the hidden hole,
using ~org-fc-type-cloze-hole-face~ to highlight it.

Card titles can contain holes, too.

Positions: =0=, =1=, ...

Cloze cards can have a number of sub-types.

** Deletion ~'deletion~
Only one hole is hidden.
** Enumerations ~'enumeration~
All holes *behind* the currently review one are hidden, too.

Useful for memorizing lists where the order of items is important.
** Single ~'single~
All holes besides the current one are hidden.

Useful for learning syntax or function names of a programming language
by using a =src= block in the card and marking parts of the code as
holes.
** Context ~'context~
Holes ~org-fc-type-cloze-context~ (default 1) around the currently
reviewed one are shown.

Useful for memorizing longer lists where the order of items is important.
** Hole Syntax
Deletions can have the following forms

- ~{{text}}~
- ~{{text}@id}~
- ~{{text}{hint}}~
- ~{{text}{hint}@id}~

~text~ should not contain any "}",
unless it is part of a ~$latex$~ block.
In this case, ~latex~ should not contain any "$".

Holes *inside* latex blocks are not handled correctly at the moment.
As a workaround, create multiple smaller latex blocks and wrap each in
a hole.
** Image Deletions
Due to an issue with invisible overlays, images inside cloze-holes are
not shown correctly during review if the image link directly follows
the opening ~{{~.

Adding spaces around the image link fixes this problem,
e.g. ~{{ [[file:my_image.png]] }}~.
** LaTeX in Cloze Deletions
LaTeX code in cloze delections can't contain a ~}}~,
to work around this limitation, insert a space between the braces.

Example: ~\frac{1}{\sqrt{2} }~
* Compact Cards
For cards without a "Back" heading, the headline text is considered as
the front, the main text as the back.

This is useful for cards with a short front text, e.g. when learning
definitions of words.
* Defining Own Card Types
To define a custom card type,
you need to implement three functions:

- ~(...-init)~ to initialize a heading as a flashcard of this type,
  setting up the cards properties & review data.
  Should be marked as ~(interactive)~.
- ~(...-setup position)~ to setup ~position~ of the card for review
- ~(...-flip)~ to flip the card
- ~(...-update)~ to update the review data of the card, e.g. if a new
  hole is added to a cloze card

All of these are called with ~(point)~ on the cards heading.

A docs/components.org => docs/components.org +66 -0
@@ 0,0 1,66 @@
#+TITLE: Components
#+DATE: [2020-08-27 Thu 11:55]
#+KEYWORDS: fc

Components of org-fc and the most important functions they expose.

* Overview
#+begin_src plantuml :file images/components.png
[Index / Query] -* [Dashboard]
[Index / Query] -* [Review]
[Index / Query] -- [AWK]
[Review] -- [Card Types]
[Review] -- [SM2 Spacing Algorithm]
[Card Types] -- [Normal]
[Card Types] -- [Double]
[Card Types] -- [Text Input]
[Card Types] -- [Cloze]
[Text Input] -- [Diff]
#+end_src

#+RESULTS:
[[file:images/components.png]]

* ~org-fc-core.el~
Core functions.
* ~org-fc-index.el~
Takes care of indexing and filtering cards.

- ~(org-fc-index context)~ (context is a [[file:review_contexts.org][Review Context]])
- ~(org-fc-index-positions index)~
- ~(org-fc-index-shuffled-positions index)~

- ~(org-fc-index-compile-filter filter)~, turns ~filter~ into a lambda
  function

** ~org-fc-awk.el~
AWK based indexer.
* ~org-fc-sm2.el~
Implementation of the SM2 [[file:repetition_spacing_algorithm.org][Repetition Spacing Algorithm]].

- ~(org-fc-sm2-next-parameters ease box interval rating)~
  returns a list ~(next-ease next-box next-interval)~

* ~org-fc-review.el~
Review functionality of org-fc.

- ~(org-fc-review context)~ where ~context~ is a [[file:review_contexts.org][Review Context]]
- ~(org-fc-demo)~ starts a review of the demo file
* ~org-fc-diff.el~
Diff functions for (single-line) strings.

- ~(org-fc-diff got expected)~
  returns a pair ~(got . expected)~ of colored strings.
* ~org-fc-dashboard.el~
Dashboard for org-fc.

- ~(org-fc-dashboard context)~ where ~context~ is a [[file:review_contexts.org][Review Context]]
* Card Types
** ~org-fc-type-normal.el~
Simple front -> back card type.
** ~org-fc-type-double.el~
Bidirectional card type (front <-> back).
** ~org-fc-type-text-input.el~
Text input cards.
** ~org-fc-type-cloze.el~
Card type for cloze deletions.

A docs/customizing_org-fc.org => docs/customizing_org-fc.org +14 -0
@@ 0,0 1,14 @@
#+TITLE: Customizing Org-Fc
#+DATE: [2020-08-05 Wed 15:28]
#+KEYWORDS: fc

For an overview of customization options,
~M-x customize~ and search for ~org-fc~.

* Hooks
- ~org-fc-before-setup-hook~
  Runs before a card is set up for review
- ~org-fc-after-setup-hook~
  Runs after a card is set up for review
- ~org-fc-after-review-hook~
  Runs when the review ends / is quit

A docs/dashboard.org => docs/dashboard.org +15 -0
@@ 0,0 1,15 @@
#+TITLE: Dashboard
#+DATE: [2020-08-05 Wed 15:29]
#+KEYWORDS: fc

~M-x org-fc-dashboard~ shows a buffer with statistics for review
performance and cards / card types.

[[file:images/dashboard.png]]

Review performance statistics are calculated based on
the [[file:review_history.org][Review History]]. Only cards with a box >=
~org-fc-stats-review-min-box~ (default: 0) are included.

Setting this to a higher value (e.g. 2) excludes the first few
"learning" reviews of a card.

A docs/differences_from_other_flashcard_systems.org => docs/differences_from_other_flashcard_systems.org +37 -0
@@ 0,0 1,37 @@
#+TITLE: Differences from Other Flashcard Systems
#+DATE: [2020-07-17 Fri 00:42]
#+KEYWORDS: fc

There are a few other packages implementing a SRS (spaced repetition
system) based on org-mode.

Below, I've listed a the ones I've found so far that are actively
maintained and implement a lot of useful functionality.

Thanks to the maintainers and all contributors for their work on these
projects!

* Other (Open Source) SRS
- [[https://apps.ankiweb.net/][Anki]]
- [[https://mnemosyne-proj.org/][Mnemosyne Project]]

The [[file:repetition_spacing_algorithm.org][Repetition Spacing Algorithm]] of org-fc is very similar to the one
used in Anki.

When working with a large collection of mostly text-based items,
it's important to have powerful editing possibilities.

org-fc is a SRS built into a (the) most powerful text editor.
* Org-Mode
- [[https://gitlab.com/phillord/org-drill/][phillord/org-drill]]
- [[https://github.com/abo-abo/pamparam][abo-abo/pamparam]]

Among the other org-mode based SRS I've found so far,
org-fc is unique in that each headline can have multiple "positions"
that are reviewed independently from each other.

This is very useful for cloze deletions.
* Memrise                                                          :noexport:
What is does well:
- presenting cards in different directions / contexts
- repeating forgotten cards in different ways

A docs/extensions.org => docs/extensions.org +36 -0
@@ 0,0 1,36 @@
#+TITLE: Extensions
#+DATE: [2020-07-19 Sun 16:06]
#+KEYWORDS: fc

Org-fc comes with a number of extensions that are not enabled by default.

* ~org-fc-audio~
Can be enabled with ~(require 'org-fc-audio)~.

Adds audio attachments for cards that are played during review,
either before or after a card is set up.
(This distinction is relevant for text-input cards).

Files are played using the ~mpv~ media player.

Commands:
- ~org-fc-audio-set-before~
- ~org-fc-audio-set-after~
* ~org-fc-keymap-hint~
Can be enabled with ~(require 'org-fc-keymap-hint)~.

Shows a list of available key bindings during the review,
to recreate the look & feel of the previous hydra-based implementation.

- ~[RET] flip [q] quit [s] suspend-card~
- ~[a] rate-again [h] rate-hard [g] rate-good [e] rate-easy [s] suspend-card [q] quit~
* ~org-fc-hydra~
A hydra for accessing commonly used org-fc commands and for marking
headlines as flashcards.

It can be loaded and bound to a hotkey like this:

#+begin_src emacs-lisp
  (require 'org-fc-hydra)
  (global-set-key (kbd "C-c f") 'org-fc-hydra/body)
#+end_src

A docs/hydra.org => docs/hydra.org +6 -0
@@ 0,0 1,6 @@
#+TITLE: Hydra
#+DATE: [2020-08-05 Wed 15:31]
#+KEYWORDS: fc

The org-fc hydra provides a quick way of interacting with the
flashcard system.

A docs/incubator.org => docs/incubator.org +18 -0
@@ 0,0 1,18 @@
#+SETUPFILE: ~/org/setup.org
#+TITLE: Incubator
#+DATE: [2020-07-17 Fri 00:33]
#+KEYWORDS: fc

* Presentations using org-mode
- Cloze-like cards for revealing points of a list
* Workout Tracker / Timer
One example would be storing one exercise per heading, using the
positions to store one or more sets and logging the number of
repetitions done on each "review".

- TTS for instructions
- Exercise / Pause Timer
* Mood Tracking
- Store History in table instead of CSV file
* Sharing Support, Merging of Changed Cards
1. strip review data, creation date

A docs/index.org => docs/index.org +66 -0
@@ 0,0 1,66 @@
#+TITLE: Org Flashcards
#+DATE: [2020-07-17 Fri 00:16]
#+KEYWORDS: fc

- [[https://git.sr.ht/~l3kn/org-fc][Git]]
- [[https://lists.sr.ht/~l3kn/org-fc][Mailing List]]
- [[https://todo.sr.ht/~l3kn/org-fc][Issue Tracker]]

[[file:images/review.png]]

* Introduction
Org-fc is a spaced-repetition system for Emacs' org-mode.

It allows you to mark headlines in a file as "flashcards", turning
pieces of knowledge you want to learn into a question-answer test.

These cards are reviewed at regular interval. After each review, a
[[file:repetition_spacing_algorithm.org][Repetition Spacing Algorithm]] is used to calculate the next interval
based on how well you remembered the contents of the card.
* Getting Started
Start by [[file:installation.org][installing org-fc]] using the package manager of your choice.

A file demonstrating all [[file:card_types.org][Card Types]] is included. ~M-x org-fc-demo~
starts a review of this file.

To create your own flashcards, create a heading in an org-mode file
and [[file:marking_headings_as_cards.org][mark it as a flashcard]], using either one of the
~org-fc-type-...-init~ commands (e.g. ~org-fc-type-normal-init~)
or the [[file:hydra.org][org-fc Hydra]] (e.g. =C-c f= to open it, =t= to initialize a
new card, =n= to select the normal card type).

Once you've created a bunch of cards, you can start a [[file:review.org][Review Session]]
with ~M-x org-fc-review~ (=C-c f r= in the hydra).

~M-x org-fc-dashboard~ (=C-c f m= in the hydra) opens a [[file:dashboard.org][Dashboard]]
with statistics on the flashcards in the system.

Before reviews and when opening the dashboard, you're asked to select
a [[file:review_contexts.org][Review Context]]. These can be used to group cards to review them
separately from each other, e.g. when learning multiple languages.

Note 1: The [[file:hydra.org][Hydra]] is not enabled by default, add ~(require
'org-fc-hydra)~ to your configuration to load it.

Note 2: Make sure to check out [[file:use_with_evil-mode.org][Use with Evil-Mode]] if you're using
evil-mode.

Note 3: Before starting the review, make sure to add the directory of
your org files to ~org-fc-directories~, e.g. via ~(setq
org-fc-directories '("/my-org-files/"))~
* Design Goals / Choices
- [[file:differences_from_other_flashcard_systems.org][Differences from Other Flashcard Systems]]
- Good [[file:performance.org][Performance]]
  - =awk= is used for quickly finding cards due for review,
    instead of relying on the slow org-element parser
- Support for multiple *positions* in a card / heading
- All relevant data kept in org files for easy version control
- Review directly on the source org file for easy editing of cards
  during review
* Advanced Topics
- [[file:components.org][Components]]
- [[file:alternative_applications.org][Alternative Applications]]
- [[file:extensions.org][Extensions]]
- [[file:customizing_org-fc.org][Customizing Org-Fc]]
* License
Copyright © Leon Rische and contributors. Distributed under the GNU General Public License, Version 3

A docs/installation.org => docs/installation.org +123 -0
@@ 0,0 1,123 @@
#+TITLE: Installation
#+DATE: [2020-08-05 Wed 15:31]
#+KEYWORDS: fc

Before using this package, ~org-fc-directories~ should be set to the
directory to search for org files containing flashcards.

The file used to store the review history can be customized with
~org-fc-review-history-file~ and defaults to ~/path/to/config/org-fc-reviews.tsv~.

This package is not (yet) available on MELPA / ELPA,
to install it, clone the repository (e.g. to ~src/org-fc/~)
and follow the setup instructions.

* Dependencies
Org-fc has been tested with org-mode version 9.3.6 and gawk version
5.1.0. You can check your versions with ~M-x org-version~
and ~gawk -V~.

- The =gawk= extension of =awk= for extracting review data from =.org= files
- =find= for finding all org files in the ~org-fc-directories~
- =xargs= for processing files in parallel
** Linux / UNIX
=find= and =xargs= should be included in most distributions, =gawk=
can be installed from source or using your package manager of choice.

Examples:
- =pacman -S gawk= (Arch Linux)
- =apt-get install gawk= (Ubuntu, Debian, ...)
- =yum install gawk= (CentOS)

For more information, see [[https://www.gnu.org/software/gawk/manual/html_node/Installation.html][gawk manual - Installation]].
** MacOS
On MacOS all dependencies can be installed using [[https://www.macports.org/][macports]] or [[https://brew.sh/][homebrew]].
=find= and =xargs= are part of the =findutils= package.

- =port install gawk findutils=
- =brew install gawk findutils=

You might have to adjust your =$PATH= to make sure Emacs can find all
relevant binaries.

#+BEGIN_SRC
  export PATH="/opt/local/libexec/gnubin:/opt/local/bin:$PATH"
#+END_SRC

For more information, refer to the documentation of macports /
homebrew.
* Manual Installation
#+begin_src bash
  cd ~/src/
  git clone https://git.sr.ht/~l3kn/org-fc
#+end_src

#+BEGIN_SRC emacs-lisp
  (add-to-list 'load-path "~/src/org-fc/")

  (require 'org-fc)
  (require 'org-fc-hydra)

  (setq org-fc-directories '("~/org/"))
#+END_SRC
* Setup with [[https://github.com/jwiegley/use-package/][use-package]]
Assuming you've manually cloned the repository.

#+BEGIN_SRC emacs-lisp :eval no-export
  (use-package hydra)
  (use-package org-fc
    :load-path "~/src/org-fc"
    :custom (org-fc-directories '("~/org/"))
    :config
    (require 'org-fc-hydra))
#+END_SRC

Or, using [[https://github.com/raxod502/straight.el/][straight.el]]:

#+BEGIN_SRC emacs-lisp :eval no-export
  (use-package hydra)
  (use-package org-fc
    :straight
    (org-fc
     :type git :repo "https://git.sr.ht/~l3kn/org-fc"
     :files (:defaults "awk" "demo.org"))
    :custom
    (org-fc-directories '("~/org/"))
    :config
    (require 'org-fc-hydra))
#+END_SRC

Note that in this case, you don't have to clone the repository.
* Setup with [[https://github.com/raxod502/straight.el/][straight.el]]
#+BEGIN_SRC emacs-lisp :eval no-export
  (straight-use-package 'hydra)
  (straight-use-package
   '(org-fc
     :type git :repo "https://git.sr.ht/~l3kn/org-fc"
     :files (:defaults "awk" "demo.org")
     :custom (org-fc-directories '("~/org/"))
     :config
     (require 'org-fc-hydra)))
#+END_SRC

* Setup with [[https://github.com/syl20bnr/spacemacs/][spacemacs]]
You don't need to manually clone the repository,
just put this in your =.spacemacs=:

#+BEGIN_SRC emacs-lisp :eval no-export
  ;; ...
  dotspacemacs-additional-packages
  '((org-fc
     :location (recipe :fetcher git
                       :url "https://git.sr.ht/~l3kn/org-fc"
                       :files (:defaults "awk" "demo.org"))))
  ;; ...
  (defun dotspacemacs/user-config ()
    ;; ...
    ;; Org-fc
    (use-package hydra)
    (require 'org-fc-hydra)
    (setq org-fc-directories '("~/org/"))
    ;; ...
    )
#+END_SRC

A docs/introduction.org => docs/introduction.org +53 -0
@@ 0,0 1,53 @@
#+TITLE: Introduction
#+DATE: [2020-07-17 Fri 01:07]
#+FILETAGS: :fc-demo:
#+KEYWORDS: fc

* Welcome to org-fc                                            :suspended:fc:
:PROPERTIES:
:FC_CREATED: 2020-07-16T23:14:28Z
:FC_TYPE:  normal
:ID:       78877a24-22f3-4996-8fc1-544204cda0b0
:END:
:REVIEW_DATA:
| position | ease | box | interval | due                  |
|----------+------+-----+----------+----------------------|
| front    |  2.5 |   0 |        0 | 2020-07-16T23:14:28Z |
:END:
This is an interactive introduction into org-fc,
using org-fc.

You're currently (re)viewing the front of a flashcard.
To "flip" it, revealing the contents of its "Back" heading,
press the enter key.

You can quit the review at any time by pressing "q".
** Back
This is the back side of the flashcard.

You can now rate how well you remembered the information on the back
side of the card.
* The "normal" Card Type                                       :suspended:fc:
:PROPERTIES:
:FC_CREATED: 2020-07-16T23:20:50Z
:FC_TYPE:  normal
:ID:       8f01a1c9-c842-48b8-9952-ca1c38875703
:END:
:REVIEW_DATA:
| position | ease | box | interval | due                  |
|----------+------+-----+----------+----------------------|
| front    |  2.5 |   0 |        0 | 2020-07-16T23:20:50Z |
:END:
Let's try that again!

What are the two sides of a "normal" flashcard
like the one you have just seen?
** Back
- front (i.e. question)
- back (i.e. answer)
* WAITING Use inline-evaluation for key bindings                   :noexport:
:PROPERTIES:
:ID:       5f3f2a87-d7b9-409c-af97-691643f07b4d
:END:
- Requires inline-evaluation / insertion of lisp code
- Similar to org (export) macros

A docs/marking_headings_as_cards.org => docs/marking_headings_as_cards.org +47 -0
@@ 0,0 1,47 @@
#+TITLE: Marking Headings As Cards
#+DATE: [2020-08-07 Fri 14:34]
#+KEYWORDS: fc

A *card* is an org-mode headline with a =:fc:= tag attached to it.
Each card can have multiple *positions* reviewed independently from
each other, e.g. one for each hole of a cloze card.

Review data (ease, interval in days, box, due date) is stored in a table
in a drawer inside the card.

#+BEGIN_EXAMPLE
  :REVIEW_DATA:
  | position | ease | box | interval | due                    |
  |----------+------+-----+----------+------------------------|
  |        2 | 2.65 |   6 |   107.13 |    2020-04-07T01:01:00 |
  |        1 | 2.65 |   6 |   128.19 |    2020-04-29T06:44:00 |
  |        0 | 2.95 |   6 |   131.57 |    2020-04-30T18:03:00 |
  :END:
#+END_EXAMPLE

The [[file:review_history.org][Review History]] is stored in a TSV file to avoid cluttering the org
files.

Each card needs at least two properties, an *unique* ~:ID:~ and a
~:FC_TYPE:~. In addition to that, the date a card was created
(i.e. the headline was marked as a flashcard) is stored to allow
creating statistics for how many cards were created in the last day /
week / month.

#+BEGIN_EXAMPLE
  :PROPERTIES:
  :ID:       4ffe66a7-7b5c-4811-bd3e-02b5c0862f55
  :FC_TYPE:  normal
  :FC_CREATED: 2019-10-11T14:08:32
  :END:
#+END_EXAMPLE

Card types (should) implement a ~org-fc-type-...-init~ command that
initializes these properties and sets up the review data drawer

All timestamps created and used by org-flashcards use ISO8601 format
with second precision and without a timezone (timezone UTC0).

This prevents flashcard due dates from showing up in the org-agenda
and allows filtering for due cards by string-comparing a timestamp
with one of the current time.

A docs/performance.org => docs/performance.org +14 -0
@@ 0,0 1,14 @@
#+TITLE: Performance
#+DATE: [2020-07-19 Sun 16:01]
#+KEYWORDS: fc

All user-facing commands (especially during review) should be as fast
as possible (<300ms).

Using the =awk= indexer, searching 2500 org files (~200k lines in
total) for due flashcards takes around ~500ms on my laptop (Thinkpad
L470, SSD).

Using a lisp indexer based on ~org-map-entries~,
searching a single 6500 line file with 333 flashcards takes ~1000ms,
indexing the same file with =awk= takes around ~50ms.

A docs/repetition_spacing_algorithm.org => docs/repetition_spacing_algorithm.org +6 -0
@@ 0,0 1,6 @@
#+TITLE: Repetition Spacing Algorithm
#+DATE: [2020-07-17 Fri 00:46]
#+KEYWORDS: fc

This package uses a modified version of the [[https://www.supermemo.com/en/archives1990-2015/english/ol/sm2][SuperMemo - SM2 Algorithm]]
algorithm, based on the one used by [[https://apps.ankiweb.net/docs/manual.html#what-algorithm][Anki]].

A docs/review.org => docs/review.org +102 -0
@@ 0,0 1,102 @@
#+TITLE: Review
#+DATE: [2020-08-05 Wed 15:29]
#+KEYWORDS: fc

A review session can be started with ~M-x org-fc-review~.  Due cards
are reviewed in random order.

If a card was rated "again", it will be reviewed again at the end of
the current review session.  This can be disabled by setting
~org-fc-append-failed-cards~ to ~nil~.

[[file:review_contexts.org][Review Contexts]] can be used to only review cards of a set tag or type,
e.g. when using org-fc to learn different foreign languages where
mixing them in one review session would lead to confusion.

Each time a card is rated, an entry is added to the [[file:review_histord.org][Review History]].

Cards can be excluded from review without deleting them
by [[file:suspending_cards.org][suspending them]].

* Review Process
1. Open file of card
2. Narrow to heading
3. Set up card for review
4. Activate ~org-fc-flip-mode~
5. Flip the card (user)
6. Switch to ~org-fc-rate-mode~
7. Rate the card (user)
8. Repeat process with next due card

#+begin_src plantuml :file images/review_loop.png
"Review next Card" -> if "Another card due?" then
  -->[true] "Open file of card\nNarrow\nSet up\nflip-mode"
  --> if "Action" then
    -->[flip] "Flip Card\nrate-mode"
    --> if "Action" then
      -->[again] "Append Card"
      --> "Update Review Data"
    else
      -->[hard / good / easy] "Update Review Data"
      --> "Review next Card"
    else
      -->[suspend] "Suspend Card"
      --> "Review next Card"
    else
      ->[edit] "Edit Card"
      --> "Open file of card\nNarrow\nSet up\nflip-mode"
    else
      -->[quit] "Quit Review"
    endif
  else
    -->[quit] "Quit Review"
  endif
else
  -->[false] "Quit Review"
endif
#+end_src

#+RESULTS:
[[file:images/review_loop.png]]

#+begin_src plantuml :file images/review_sequence.png
actor User
collections Card
database Index

User -> Index: Start Review
Index -> Card: Jump to next due card
Card -> Card: Narrow to card heading
Card -> Card: Set up card
User -> Card: Flip card
User -> Card: Rate card
Card -> Index: Update review data
Index -> Card: Jump to next due card
... Repeat ...
#+end_src

By default failed cards (rated again) are appended to the current
review session. This can be disabled with ~(setq
org-fc-append-failed-cards nil)~.

#+RESULTS:
[[file:images/review_sequence.png]]

* Flip Mode
| Key | Binding                  |
|-----+--------------------------|
| RET | flip card                |
| n   | flip card                |
| s   | suspend card             |
| p   | pause review for editing |
| q   | quit review              |
* Rate Mode
| Key | Binding                  |
|-----+--------------------------|
| a   | rate again               |
| h   | rate hard                |
| g   | rate good                |
| e   | rate easy                |
| s   | suspend card             |
| p   | pause review for editing |
| q   | quit review              |

A docs/review_contexts.org => docs/review_contexts.org +65 -0
@@ 0,0 1,65 @@
#+TITLE: Review Contexts
#+DATE: [2020-08-01 Sat 10:41]
#+KEYWORDS: fc

By default, two contexts are defined:

- all :: all cards in ~org-fc-directories~
- buffer :: all cards in the current buffer

New contexts can be defined by adding them to the alist
~org-fc-custom-contexts~.

Contexts have the form ~(:paths paths :filter filter)~.

- ~:paths~ (optional)
  either a list of paths, a single path
  or ~'buffer~ for the current buffer.
  Paths don't have to be included in the ~org-fc-directories~.
  Defaults to ~org-fc-directories~.
- ~:filter~ (optional), a card filter defaulting to a filter that
  matches all cards.

Filters can be combinations of the following expressions:

- ~(and ex1 ex2 ...)~
- ~(or ex1 ex2 ...)~
- ~(not ex)~
- ~(tag "tag")~
- ~(type card-type) or (type "card-type")~

* Examples
All double cards with tag "math":
#+begin_src emacs-lisp
  (add-to-list 'org-fc-custom-contexts
    '(double-math-cards . (:filter (and (type double) (tag "math")))))
#+end_src

All cards in that don't have one of the tags "foo" and "bar":
#+begin_src emacs-lisp
  (add-to-list 'org-fc-custom-contexts
    '(no-foo-bar-cards . (:filter (not (or (tag "foo") (tag "bar"))))))
#+end_src

All cards in =~/combinatorics/= or =~/number_theory.org=:
#+begin_src emacs-lisp
  (add-to-list 'org-fc-custom-contexts
    '(math-cards . (:paths ("~/combinatorics/" "~/number_theory.org"))))
#+end_src

All cards in =~/combinatorics/= with tag "theorem":
#+begin_src emacs-lisp
  (add-to-list 'org-fc-custom-contexts
    '(combinatorics-theorems .
      (:paths "~/combinatorics/" :filter (tag "theorem"))))
#+end_src

All double cards in the current buffer:
#+begin_src emacs-lisp
  (add-to-list 'org-fc-custom-contexts
    '(current-double .
      (:paths buffer :filter (type double))))
#+end_src
* Note
Because parsing of tags is done in AWK, tag filters don't work for
tags defined in the =#+FILETAGS:= of a =#+SETUP_FILE:=.

A docs/review_history.org => docs/review_history.org +23 -0
@@ 0,0 1,23 @@
#+TITLE: Review History
#+DATE: [2020-08-01 Sat 10:47]
#+KEYWORDS: fc

The review history is stored in a tsv file, to avoid cluttering org
files. This makes it easy to calculate review statistics.

Columns:
1. Date in ISO8601 format, second precision
2. Filename
3. Card ID
4. Position
5. Ease (before review)
6. Box (before review)
7. Interval (before review)
8. Rating
9. Seconds spent reviewing the card
10. [[file:repetition_spacing_algorithm.org][Repetition Spacing Algorithm]] used

More advanced review algorithms might need to use the review history
of a card. In this case, the card ID + position should be used to look
up the review history, as the filename can change when moving cards
from file to file.

A docs/suspending_cards.org => docs/suspending_cards.org +27 -0
@@ 0,0 1,27 @@
#+TITLE: Suspending Cards
#+DATE: [2020-08-01 Sat 10:55]
#+KEYWORDS: fc

Cards can be suspended (excluded from review) by adding a =suspended=
tag, either by hand or using the ~org-fc-suspend-card~ command.

The =suspended= tag is inherited, so all cards in a subtree can be
suspended by adding the tag to the parent heading, and all cards in a
file can be suspended by adding ~#+FILETAGS: suspended~ at the start.

Cards can be unsuspended using the ~org-fc-unsuspend-card~ command
or by manually removing the =suspended= tag.

It might be preferable to suspend multiple cards by adding the
=suspended= tag to each one, so they remain suspended
when moved to another headline or file.

In this case, you can use the following commands:

- ~org-fc-suspend-tree~, ~org-fc-unsuspend-tree~ for suspending all
  cards in a subtree
- ~org-fc-suspend-buffer~, ~org-fc-unsuspend-buffer~ for suspending all
  cards in the current buffer

Note that these commands don't affect filetags or tags of parent
headlines.

A docs/use_with_evil-mode.org => docs/use_with_evil-mode.org +48 -0
@@ 0,0 1,48 @@
#+TITLE: Use with Evil-Mode
#+DATE: [2020-07-19 Sun 16:03]
#+KEYWORDS: fc

The key bindings used by the review modes of org-fc conflict with
some of the bindings used by evil mode.

As a workaround, you can add minor mode keymaps for
each of the evil-mode states you're using org-fc with.

#+begin_src emacs-lisp
(evil-define-minor-mode-key '(normal insert emacs) 'org-fc-review-flip-mode
  (kbd "RET") 'org-fc-review-flip
  (kbd "n") 'org-fc-review-flip
  (kbd "s") 'org-fc-review-suspend-card
  (kbd "q") 'org-fc-review-quit)

(evil-define-minor-mode-key '(normal insert emacs) 'org-fc-review-rate-mode
  (kbd "a") 'org-fc-review-rate-again
  (kbd "h") 'org-fc-review-rate-hard
  (kbd "g") 'org-fc-review-rate-good
  (kbd "e") 'org-fc-review-rate-easy
  (kbd "s") 'org-fc-review-suspend-card
  (kbd "q") 'org-fc-review-quit)
#+end_src

* Using general.el for Keybindings
#+begin_src emacs-lisp
  (general-define-key
   :definer 'minor-mode
   :states 'normal
   :keymaps 'org-fc-review-flip-mode
   "RET" 'org-fc-review-flip
   "n" 'org-fc-review-flip
   "s" 'org-fc-review-suspend-card
   "q" 'org-fc-review-quit)

  (general-define-key
   :definer 'minor-mode
   :states 'normal
   :keymaps 'org-fc-review-rate-mode
   "a" 'org-fc-review-rate-again
   "h" 'org-fc-review-rate-hard
   "g" 'org-fc-review-rate-good
   "e" 'org-fc-review-rate-easy
   "s" 'org-fc-review-suspend-card
   "q" 'org-fc-review-quit)
#+end_src