st3v3nmw / obsidian-spaced-repetition

Fight the forgetting curve by reviewing flashcards & entire notes on Obsidian
https://www.stephenmwangi.com/obsidian-spaced-repetition/
MIT License
1.6k stars 191 forks source link

Anki Cloze syntax #93

Open domkm opened 3 years ago

domkm commented 3 years ago

Hi Stephen,

Thanks again for providing and maintaining this great plugin!

My preferred card style is Cloze because they support any number of "sides". For example, in Anki, {{c1:This text}} would {{c2:generate}} {{c3:3 cards}}. I don't use this plugin's Cloze functionality because it only supports 1 side and uses Markdown highlighting syntax, so I even avoid using highlights because I don't want them to turn into Cloze cards (it's an anti-feature for me). Would you consider supporting Anki's Cloze syntax?

st3v3nmw commented 3 years ago

Hello, I'll add that to the roadmap. Wouldn't it be better to have a more compact version like {This text} would {generate} {cards}?

st3v3nmw commented 3 years ago

& what do you mean by:

it only supports 1 side

I've also been thinking of adding options to disable certain flashcard styles the user isn't using (the regex scanning is a tad too slow).

domkm commented 3 years ago

Hello, I'll add that to the roadmap. Wouldn't it be better to have a more compact version like {This text} would {generate} {cards}?

Thanks!

No, a more compact syntax has two problems:

  1. It is very likely to conflict with other tools or notes. For example, :: conflicts with DataView (https://github.com/st3v3nmw/obsidian-spaced-repetition/issues/60) and many programming languages (https://github.com/st3v3nmw/obsidian-spaced-repetition/issues/76).
  2. It directly links the number of clozes in a card with the number of sides in a card (e.g., 3 clozes must generate 1 side or 3 sides but cannot generate 2 sides).

Here is an example that would suffer from both of the above problems: In $\sqrt[n]{a} = {a^{\frac{1}{n}}}$, $n$ is called the {index}, $a$ is called the {radicand}, and the symbol $\sqrt {}$ is called the {radical}.

  1. LaTeX (and many other syntaxes) use {} and this could be incorrectly interpreted as a cloze.
  2. index, radicand, and radical would either be part of the same cloze or generate 3 different clozes, but index should have its own cloze while radicand and radical should be in a grouped cloze because they give each other away.

What I actually want is: In $\sqrt[n]{a} = {a^{\frac{1}{n}}}$, $n$ is called the {{c1:index}}, $a$ is called the {{c2:radicand}}, and the symbol $\sqrt {}$ is called the {{c2:radical}}. (Note that c1 is index while c2 is both radicand and radical.)

domkm commented 3 years ago

& what do you mean by:

it only supports 1 side

I've also been thinking of adding options to disable certain flashcard styles the user isn't using (the regex scanning is a tad too slow).

I would appreciate and utilize these options!

st3v3nmw commented 3 years ago

Here is an example that would suffer from both of the above problems: In $\sqrt[n]{a} = {a^{\frac{1}{n}}}$, $n$ is called the {index}, $a$ is called the {radicand}, and the symbol $\sqrt {}$ is called the {radical}.

1. LaTeX (and many other syntaxes) use `{}` and this could be incorrectly interpreted as a cloze.

2. `index`, `radicand`, and `radical` would either be part of the same cloze or generate 3 different clozes, but `index` should have its own cloze while `radicand` and `radical` should be in a grouped cloze because they give each other away.

What I actually want is: In $\sqrt[n]{a} = {a^{\frac{1}{n}}}$, $n$ is called the {{c1:index}}, $a$ is called the {{c2:radicand}}, and the symbol $\sqrt {}$ is called the {{c2:radical}}. (Note that c1 is index while c2 is both radicand and radical.)

Aah, I see. I hadn't thought of that. Thanks for your input!

gh16683170 commented 3 years ago

i love this idea. i use the cloze sytax more ofthen like this: {{c1:This text}} would {{c1:generate}} {{c1:1 cards}}. i like put multiple cloze in one card. looking forward this feature~

thanks.

MatthewJMullins commented 3 years ago

Yeah, having this feature work like Anki is a good idea. For example...

If {{c1: you}} add clozes to a {{c1: card}} like this, then the the first two {{c2: will}} close together.

This makes two cards. The first card will have two clozes: "you" and "card" simultaneously. The second card will cloze text "will".

dummifiedme commented 3 years ago

I had a similar suggestion too. Maybe we can exploit the footnote syntax. For creating hints and maybe for numbered cloze as well.

Something like this This is a {cloze}^[with hint]. OR This is a ==cloze==^[with hint].

Which would be equivalent to: Q: This is a [with hint]. A: This is a cloze.

This would give us a clean note to read and have our hints too. Eg. too many symbols in the preview mode isn't really good, but with this, the hints(which are really basic words like 'year'. 'article', 'number' etc...) would be hidden from the immediate view.

Similar arrangement can be done for assigning the number of cloze. with a comma or something inside the footnote.

DaveyUS commented 3 years ago

Also interested in more in depth cloze support. Really the only functionality I need is the ability to hide multiple sections in a text block.

Your example of "==Life==, ==Liberty== and ==the pursuit of Happiness==" results in: "[...], Liberty, and the pursuit of happiness"

Ideally I'd like the ability to have all three blocked like so: "[...], [...], and [...]"

This request from @domkm, would solve this.

Thanks again for your work on this!

MotaOcimar commented 2 years ago

As @dummifiedme suggested, I would love to use footnote syntax to identify cloze numbering. Mainly because it generates a very clean and nice preview in Obsidian.

One suggestion is This ==text==[^1] will ==generate==[^2] only ==2==[^1] cards Edit mode: image Preview mode: image

So it can still have hints, like that: This ==text==[^1]^[with hint] will ==generate==[^2] only ==2==[^1] cards Edit mode: image Preview mode: image

dummifiedme commented 2 years ago

@MotaOcimar What I was suggesting helped rid of the number as well. Directly type in the hint on the footnote

MotaOcimar commented 2 years ago

Hi @dummifiedme! You are correct, you already suggested a good way to number the clozes and give tips at the same time. Sorry, I missed it But I must admit I liked the look that the last suggestion provides in edit and preview mode

Compare:

  1. First suggestion: Text: This ==text==^[1,with hint] will ==generate==^[2] only ==2==^[1] cards Edit mode: image Preview mode: image

  2. Second suggestion: Text: This ==text==[^1]^[with hint] will ==generate==[^2] only ==2==[^1] cards Edit mode: image Preview mode: image

The second suggestion seems more discreet in edit mode to me. Also, in preview mode it is clearer if a cloze has a hint or not and which clozes have the same number.

Look: Text: ==All==[^1] these ==clozes==[^1] have the same ==number==[^1] Edit mode: image Preview mode: image

The first number in the footnote indicates that the highlights belong to the same cloze

timblaktu commented 2 years ago

@dummifiedme and @MotaOcimar, in addition to hints, footnotes can provide a succinct way to define the Cloze Extra field, a.k.a. "the back of the card".

Obviously, Hint is a per-cloze thing. Extra, however, is typically a per-note thing, i.e. since we're using a single note to generate N cloze-variations of a question about the note, we can generalize the Extra (back of the card) to explain the entire note as a whole in more detail. I call this out because Extra could be defined in a shared footnote body, down where it's not cluttering up your notes. Note that this works because multiple footnote references can refer to the same footnote body.

Re: cloze numbering with footnotes, since markdown footnote identifiers can be any alphanumeric sequence, Generally speaking, the design should not require the user to use any specific numbering or naming convention...

...except in the case of trying to define each cloze's hint in the footnote id, which I think is a good idea. :-) Note, however, that cloze hints may commonly be duplicated in multiple cloze notes. So, prefixes or suffixes might need to be used to make their corresponding footnote ids unique. Or, as @MotaOcimar suggested, perhaps we use separate footnotes for hints and extra. Perhaps the convention is simply:

  1. Numeric Footnote ids are used for Extra (back of the card).
  2. Non-Numeric Footnote ids are used for hints.

Clozes that want to define a hint would use [^hint]; cloze notes that want to define back of card information must define at least one numeric footnote id [^1] and body. (If this results in implementation/parsing difficulties (since each cloze becomes its own distinct card, and perhaps card siblings might not be able to find each other's Extra field easily/efficiently), I suppose it would be acceptable to require each cloze to have the same Extra footnote.) There may be multiple footnotes on each cloze but as has been shown these are minimally visible in both edit and preview modes, which is the whole point of using footnotes.

Note that in this scheme the hint footnotes don't really need a body, since their information is conveyed through their id. However, at least on GitHub, they don't render unless there is a complete footnote reference/body pair. In the example below, I include them with empty bodies so they'll render.

Note that the footnote syntax described above doesn't need to be restricted to highlight-clozes only. In theory, you should be able to define cloze hints and Extra/card backs using highlight and/or Anki {{cloze}} syntax to define the clozes themselves. I vastly prefer the highlight form bc it sticks to standard md syntax, so I'll show that as an example below.

The ==rain==[^type_of_weather][^1] in ==Spain==[^country] falls mostly in the ==plains==[^topography].

which renders like this (sorry GitHub md doesn't seem to support highlights):

The ==rain==[^type_of_weather][^1] in ==Spain==[^country] falls mostly in the ==plains==[^topography].

The footnote bodies would look like this in markdown/edit mode:

[^1]: [Spain](https://en.wikipedia.org/wiki/Spain) is a country in southwestern [Europe](https://en.wikipedia.org/wiki/Europe) with parts of territory in the [Atlantic Ocean](https://en.wikipedia.org/wiki/Atlantic_Ocean) and across the [Mediterranean Sea](https://en.wikipedia.org/wiki/Mediterranean_Sea). Here is some [more information on Rain](https://en.wikipedia.org/wiki/Rain). Also note that the [Rain In Spain Doesn't _Always_ Stay On The Plain](https://spainguides.com/weather/rain-spain/).
[^type_of_weather]: 
[^country]:
[^topography]: 

(See bottom of comment for rendered footnotes) [^1]: Spain is a country in southwestern Europe with parts of territory in the Atlantic Ocean and across the Mediterranean Sea. Here is some more information on Rain. Also note that the Rain In Spain Doesn't Always Stay On The Plain. [^type_of_weather]: [^country]: [^topography]:

Since this issue is really about adding support for Anki cloze syntax, I might create a new issue for the footnote syntax.

Also interested in more in depth cloze support. Really the only functionality I need is the ability to hide multiple sections in a text block.

Your example of "==Life==, ==Liberty== and ==the pursuit of Happiness==" results in: "[...], Liberty, and the pursuit of happiness"

Ideally I'd like the ability to have all three blocked like so: "[...], [...], and [...]"

This request from @domkm, would solve this.

@DaveyUS, I believe this is already supported. Perhaps this aspect got implemented in another PR after your comment. I'm using v0.14.6.

st3v3nmw commented 1 year ago

Added from #552, opened by @Celeblith

Problem

Currently each cloze segment (highlighted or bolded portion of a card) creates a separate card. So if an entry has three clozes, three cards will be added to the deck, one for each cloze. Which is great sometimes as it makes creating contextual cards for language learning much more efficient. But ... Feature

It would also be nice to have an additional and alternative cloze that allows multiple cloze replacements to appear on the same card. This is useful for cards where multiple segments of the card go together. For example, when creating cards for Latin, I might make a card like:

In castrīs sunt quattuor mīlia mīlit==um== fort==ium==

In the above card, the endings of mīlitum and fortium agree with each other, so correctly answering the card would require supplying both endings at the same time. However, if I made this entry, it would instead create two separate cards, bloating my deck and making the cloze less valuable. Context: Current Workarounds

My current workaround is to make cards like this one:

In castrīs sunt quattuor mīlia mīlit_[...]_ fort_[...]_ :: In castrīs sunt quattuor mīlia mīlit_um_ fort_ium_

The problem with this workaround is that it takes much longer to make the card, the color and format of the italic blanks does't match the usual blue, non-italic clozes, and italicizing brackets sometimes does weird things to my formatting. Most of these issues are aesthetic, but multiple clozes per card would still be a nice feature nonetheless.

st3v3nmw commented 1 year ago

Added from #496, opened by @putzwasser

Is your feature request related to a problem? Please describe. If you have a cloze it hides everything within it. It would be nice to allow displaying "fill" words or other hints within the cloze. It would require a little bit of extra syntax, though.

Describe the solution you'd like Given you'd indicate a “multiple cloze” clozed completion with surrounding curly brackets you'd achieve this:

Consumer surplus is the difference between {==the maximum price== a consumer is willing to pay and the ==actual price== they do pay}

The flascard would look like:

Consumer surplus is the difference between […] a consumer is willing to pay and the […] they do pay

You'd have to answer all blanks at once and the answers get shown at once.

So, all clozes within { and } get “combined” and hidden/shown together.

As my flashcards are separated from my actual notes, I'd have no problem by adding some wrapping curly braces. For people who mix/store their “content” notes and flashcards within the same note the syntax could be %%{%%Your ==multi== clozed ==completion==%%}%%

This is more verbose, but it hides the curly braces if you're just reading the note.

lsolesen commented 1 year ago

In Obsidian_To_Anki-plugin the cloze deletion is implemented using this regex.

((?:.+\n)*(?:.*\{\{[^\{\}]*::[^\{\}]*\}\}.*)(?:\n(?:^.{1,3}$|^.{4}(?<!<!--).*))*)

From this Github issue

xh542428798 commented 1 year ago

Sir, I found someone else had soved this problem, aosr, this plugin based on obsidian-spaced-repetition can recognize tag "#multicloze" to display multicloze, but other functions in that plugin changed a lot, I didn't like it, I beg you to incorporate "#multicloze" function from aosr, thanks a lot.

MotaOcimar commented 1 year ago

I'm working on an implementation to solve this issue. I'm kind of busy these days, so I can't promise this will be finished soon. But I hope to have a solution here 🙌

Stefanuk12 commented 1 year ago

Here's an Anki cloze parser, inspired by the original source in pure TS. There is currently one bug that needs to be addressed and then can be copy-pasted into the plugin (view more information at the bottom of the file). I've tested the code against Anki's test cases and everything else seems to work though. The code probably can be optimised a little too. Hopefully someone can do some more work on getting this to work, I will be working on it later but not sure when so posting it here...

test.zip

Edit: Thanks to a previous suggestion using regex, here is a full working parser (add onto the end of test file). I will create a PR later...

// Converts text to cards
const regex = /((?:.+\n)*(?:.*\{\{[^\{\}]*::[^\{\}]*\}\}.*)(?:\n(?:^.{1,3}$|^.{4}(?<!<!--).*))*)/gm;
function text_to_cards(text: string): Card[] {
    let cards: Card[] = [];

    for (const match of text.match(regex)) {
        const cards_match = tokens_to_cards(parse_anki_tokens(text_anki_tokens(match)));
        cards = cards.concat(cards_match);
    }

    return cards;
}

// Testing
console.log(text_to_cards("foo\n\nhello\nstuff {{c1::Canberra was {{c2::founded}} {{c2::hello}}::hint}} in 1913"))
MotaOcimar commented 7 months ago

Yay! I've finally opened a PR (#943) to address this issue. Your feedback would be greatly appreciated. It's a draft PR, so it's still a work in progress. But I believe it's now possible to run some tests and experiment with it a bit.

Please let me know what you think!

st3v3nmw commented 1 month ago

Hi,

I've just made a release v1.13.0 that ships with custom cloze patterns 🥳 all thanks to #943. The updated documentation is live here.

I've tested the release but not as extensively as I normally would. Perhaps we need a beta version of the plugin? That being said, I'm on standby to fix bugs ASAP. Just give me a shout.

Cheers.